poj 3685 Matrix 【二分】
<题目链接>
题目大意:
给你一个n*n的矩阵,这个矩阵中的每个点的数值由 i2 + 100000 × i + j2 - 100000 × j + i × j 这个公式计算得到,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
作者:is_ok
出处:http://www.cnblogs.com/00isok/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。