POJ3685 二分求第k小的值

来源:互联网 发布:久其通用数据平台 编辑:程序博客网 时间:2024/06/05 06:17

二分的题目写了好久


Description

Given a N × N matrix A, whose element in the i-th row and j-th column Aij is an number that equals i2 + 100000 × i + j2 - 100000 × j + i × j, you are to find the M-th smallest element in the matrix.

Input

The first line of input is the number of test case.
For each test case there is only one line contains two integers, N(1 ≤ N ≤ 50,000) and M(1 ≤ M ≤ N × N). There is a blank line before each test case.

Output

For each test case output the answer on a single line.

Sample Input

121 12 12 22 32 43 13 23 83 95 15 255 10

Sample Output

3-99993312100007-199987-99993100019200013-399969400031-99939
首先根据这个函数,我们可以知道是关于i递增的;

因此,枚举每一列,在每一列上使用二分答案的方法

另外求第K小的数,这里有一个二分的算法

假设有一个函数cnt能够知道比M小的数有多少个,那个我们就可以进行二分了

while (right-left>1) {            mid = (left + right) / 2;            long long tmp = cntr(mid);            if (tmp <= m-1) {                left = mid;            } else {                right = mid;            }            cnt++;        }

而恰好这里cnt也可以利用函数的单调性,进行二分

因此不会TE

#include <cstdio>#include <iostream>#include <cstring>#include <queue>#include <map>#include <cstdlib>#include <deque>#include <algorithm>#include <cmath>#include <set>#define INF 1e12using namespace std;#define N 50007long long n,m;long long maxer = -INF;long long miner = INF;long long f(long long i, long long j) { //对于x是单调递增的,对于y我们认为不确定,其实这里是先简后增的。    return ((i*i) + (100000 * i) + (j*j) - (100000*j) + (i*j));}long long upper_iter(long long col, long long lo, long long hi, long long target) {//统计某一列中小于target的个数有多少    long long res;    while(lo<hi){        long long mid=lo+(hi-lo)/2;        if((f(mid, col))<target){            lo=mid+1;            res=lo;        }else {            hi=mid;            res=hi;        }    }    return res;}long long cntr(long long M) {    //统计矩阵中<M的数有多少个    long long ans = 0;    for (long long i=1;i<=n;i++) { //每一列        ans += upper_iter(i, 1, n+1, M) - 1;    }    return ans;}int main() {    //freopen("in.txt", "r", stdin);    long long cases;    cin >> cases;    while (cases--) {        maxer = -INF;        miner = INF;        cin >> n >> m;        //最小值一定在第一行,最大值一定在最后一行        long long left = -INF;        long long right = INF;        long long cnt = 0;        long long mid;        while (right-left>1) {            mid = (left + right) / 2;            long long tmp = cntr(mid);            if (tmp <= m-1) {                left = mid;            } else {                right = mid;            }            cnt++;        }        cout << left << endl;    }}
另外注意的一点是爆longlong了,刚开始没有注意总是WA,后来对拍了一下发现了问题,并且那个最大值和最小值分别设置成1e12比较合适


0 0