POJ 3685 Matrix 二分

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LMengi000/article/details/83626874

POJ 3685 Matrix 二分 

题意:首先分析 i^{2} + 100000 × i +j^{2} - 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;
}

猜你喜欢

转载自blog.csdn.net/LMengi000/article/details/83626874