poj 3685 Matrix 【二分】

<题目链接>

题目大意:

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

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

#include <cstdio>
#include <cstring>

typedef long long ll;
ll n,m;

ll calculate(ll i,ll j){
    return i*i+100000*i+j*j-100000*j+i*j;
}

bool juge(ll x){
    ll sum_smaller=0;
    for(int j=1;j<=n;j++){ //枚举每一列,然后根据每一列的单调性,得到每一列小于x的数的个数,最后得到这个矩阵所有小于x的数的个数
        ll l=0,r=n+1;
        while(r-l>1){
            ll mid=(l+r)>>1;
            if(calculate(mid,j)<x)l=mid; //这里不能用<=,因为求的是比x小的数的个数
            else r=mid;
        }
        sum_smaller+=l;
    }
    return sum_smaller<m;
}

int main(){
    int T;scanf("%d",&T);
    while(T--){
        scanf("%lld%lld",&n,&m);
        ll l=-100000*n,r=3*n*n+100000*n;
        while(r-l>1){      //先二分枚举本题答案
            ll mid=(l+r)>>1;
            if(juge(mid))l=mid;
            else r=mid;
        }
        printf("%lld\n",l);
    }
    return 0;
}

2018-09-21

猜你喜欢

转载自www.cnblogs.com/00isok/p/9686300.html