POJ 3685 Matrix(二分答案+枚举+二分查找)

链接:传送到老年POJ

题意:

有一个 N N ( N 5 1 0 4 ) N*N(N \leq 5*10^4) 的矩阵, ( i , j ) (i, j) 位置的元素值为$ i^2 + 10^5 * i + j^2 - 10^5 * j + i * j ,求这个矩阵里的第 M$大元素。

思路:

考试的时候没有想到,玄学二分加枚举再二分。

首先在矩阵里发现单调性(行啊列啊斜着啊各种胡乱找规律)。对于这个元素值的表达式,一个有两个变量的二次多项式,可以想着把某一个变量看成常量,然后对于一个变量就比较好分析了。

所以首先我们看某一行,即把i看成常数: j 2 + ( i 1 0 5 ) j + i 2 + 1 0 5 i j^2+(i-10^5)*j+i^2+10^5*i ,然而这个二次函数在j大于0的情况下并没有什么性质,因为对称轴在y轴右边,所以没单调性。

再看一列,同理: i 2 + ( i + 1 0 5 ) i + j 2 1 0 5 j i^2+(i+10^5)*i+j^2-10^5*j ,单调性出现了,因为对称轴在y轴左边。所以在某一列中寻找小于等于某个值的时候,就可以二分了。

然后想如何用上这个单调性。可以先二分答案,对于这个答案,求出每一列小于等于他的数的个数,数量等于M就得到答案啦。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
ll T, n, m;

inline ll Cal(ll x, ll y)
{
    return x*x+x*1e5+y*y-y*1e5+x*y;
}

inline ll Count(ll x, ll y)
{
    ll l, r, mid, ans = 0;
    l = 1; r = n;
    while (l <= r){
        mid = (l+r)>>1;
        if (Cal(mid, x) <= y)
            ans = mid, l = mid+1;
        else
            r = mid-1;
    }
    return ans;
}

inline ll Check(ll sta)
{
    ll ret = 0;
    for (ll j = 1; j <= n; j++)
        ret += Count(j, sta);
    return ret;
}

ll Gao(ll m)
{
    ll l, r, mid, ans;
    l = -8e9+7;
    r = 8e9+7;
    while (l <= r){
        mid = (l+r)>>1;
        if (Check(mid) >= m)
            ans = mid, r = mid-1;
        else l = mid+1;
    }
    return ans;
}

int main()
{
    // cout << log2(1e10)*log2(5e4)*5e4 << endl;
    // ll maxn = -1e18+10, minn = 1e18+10;
    // for (int i = 1; i <= 5e4; i++)
    //     maxn = max(maxn, Cal(5e4, i)), minn = min(minn, Cal(1, i));
    // cout << maxn << " " << minn << endl;
    
    scanf("%lld", &T);
    while (T--){
        scanf("%lld%lld", &n, &m);
        printf("%lld\n", Gao(m));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyyxyyx/article/details/82668131