版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LMengi000/article/details/83626874
POJ 3685 Matrix 二分
题意:首先分析 + 100000 × i + - 100000 × j + i × j 这个式子,可以发现当j固定时,整个式子的值是递增的,因此可以得出一个结论:在矩阵的同一列中,从上往下数值是递增的,因此可以利用二分枚举每一列的值。
二分的核心思想:求数组中的第m小的数y,转换成求数组中<x的数量>=m的最小x,则y=x-1(待会证明), 所以该题二分套二分的核心思想就是:首先第第一层外层二分法枚举x,使得数组中<x的数量>=m(即合法),从而无限逼近直到得出最小的x,然后第二层内层层再用二分法求出在数组中<x的数量的个数,这个地方又是一个典型的二分,将求出在数组中<x的个数转换为求出值>=x的最小的下标p,那么<x的最大的下标q就是该下标减1即q=p-1了,然后统计个数就好。
但是这两层二分都各有一个需要注意的问题:
1.第一层二分中为什么y==x-1?因为假设y!=x-1,那么存在一个小于x的数x-1,使得
<x-1的数量>=m与x是最小值矛盾,固得证。其实这也是整数性质的体现,因为可以这样看,求一个序列中的第m小的数值,则比该值+1的数值w(二分枚举出的,不管数组中存不存在),在数组中<w的数值是有m个的,那么w-1便是该第m小的数值。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
LL n,m;
LL judge(LL i,LL j)
{
return i*i+100000*i+j*j-100000*j+i*j;
}
LL check(LL k)
{
LL cnt=0;
for(LL i=1;i<=n;i++)
{
LL l=0,r=n+1;
while(r-l>1)
{
LL mid=(l+r)>>1;
if(judge(mid,i)>=k)
r=mid;
else
l=mid;
}
cnt+=l;
}
return cnt;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld",&n,&m);
LL l=-100000*n,r=n*n+100000*n+n*n*2;
while(r-l>1)
{
LL mid=(l+r)>>1;
if(check(mid)>=m)
r=mid;
else
l=mid;
}
printf("%lld\n",l);
}
return 0;
}