二分套二分_poj3685
问题描述
Given a N × N matrix A, whose element in the i-th row and j-th column Aij is an number that equals i^2 + 100000 × i + j^2 - 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
12
1 1
2 1
2 2
2 3
2 4
3 1
3 2
3 8
3 9
5 1
5 25
5 10
- 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 - 14
 
Sample Output
3
-99993
3
12
100007
-199987
-99993
100019
200013
-399969
400031
-99939
- 1
 - 2
 - 3
 - 4
 - 5
 - 6
 - 7
 - 8
 - 9
 - 10
 - 11
 - 12
 - 13
 
分析:
这个问题也是典型的查找第k大值,所以依然用二分法,和POJ-3579这题有着异曲同工之处。 
分析一下这个式子i^2 + 100000 × i + j^2 - 100000 × j + i × j 
我们发现如果定j的话,式子随着i单调递增,那么这样就可以先循环j,内部用二分i来统计小于d的共有多少个数,然后最后判断是否小于M,小于M就是d弄的小了。二分套二分
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
using namespace std;
ll n,m;
ll f(ll x,ll y)
{
    return x*x+100000*x+y*y-100000*y+x*y;
}
bool c(ll x)
{
    ll sum=0;
    for(int j=1;j<=n;j++)
    {
        ll left=0,right=n+1;
        while(right-left>1)
        {
            ll Mid=(right+left)/2;
            if(f(Mid,j)<x)left=Mid;
            else right=Mid;
        }
        sum+=left;
    }
    return sum<m;
}
void solve()
{
    ll l=-100000*n,r=n * n + 100000 * n + n * n + n * n;
    while(r-l>1)
    {
        ll mid=(l+r)/2;
        if(c(mid))l=mid;
        else r=mid;
    }
   printf("%lld\n",l);
}
int main()
{
    int tt;
    scanf("%d",&tt);
    while(tt--){
    scanf("%d%d",&n,&m);
    solve();
    }
    return 0;
}
                    
                
                
            
        
浙公网安备 33010602011771号