<题目链接>
题目大意:
给你一个n*n的矩阵,这个矩阵中的每个点的数值由 i2 + 100000 × i + j2 - 100000 × j + i × j 这个公式计算得到,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