poj 3685 Matrix 【二分】

<题目链接>

题目大意:

给你一个n*n的矩阵,这个矩阵中的每个点的数值由   i2 + 100000 × i + j2 - 100000 × j + i ×  这个公式计算得到,N(1 ≤ N ≤ 50,000),现在问你,这个矩阵中第m小的数是多少?

解题分析:
仔细研究这个式子不难发现,在每一列,即 j 一定的时候,这个式子的数值随着 i 的增大而增大,也就是说,在这个矩阵的每一列,式子满足从上自下递增的规律,符合单调性。我们由此可以想到二分,先二分答案,即二分出第m 小的数数值为多少,然后再根据二分出的答案mid,遍历一遍矩阵的每一列,对每一列进行二分,快速求得每一列数值小于mid 的数的个数,然后将它们相加,以此来判断二分出的答案的正确性。

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 typedef long long ll;
 5 ll n,m;
 6 ll calculate(ll i,ll j){
 7     return i*i+100000*i+j*j-100000*j+i*j;
 8 }
 9 bool check(ll x){
10     ll sum_smaller=0;
11     for(int j=1;j<=n;j++){
12         ll l=0,r=n;
13         ll ans=0;
14         while(l<=r){
15             ll mid=(l+r)>>1;
16             if(calculate(mid,j)<x)ans=mid,l=mid+1;  //因为后面要根据小于x的数的个数,来判断x是否为第m小的数,所以这里只取<x的数,用"<" 
17             else r=mid-1; 
18         }
19         sum_smaller+=ans;
20     }
21     return sum_smaller<m;
22 }
23 int main(){
24     int T;scanf("%d",&T);
25     while(T--){
26         scanf("%lld%lld",&n,&m);
27         ll l=-100000*n,r=3*n*n+100000*n;
28         ll ans;
29         while(l<=r){
30             ll mid=(l+r)>>1;
31             if(check(mid))ans=mid,l=mid+1;  //当比mid小的数<m个时,继续二分,直到恰好比mid小的数的个数>=m时,此时枚举的mid一定是矩阵中的数
32             else r=mid-1;
33         }
34         printf("%lld\n",ans);
35     }
36     return 0;
37 }

 

 

2018-09-21

posted @ 2018-09-21 14:18  悠悠呦~  阅读(170)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end