二分套二分_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
",l);
}
int main()
{
    int tt;
    scanf("%d",&tt);
    while(tt--){
    scanf("%d%d",&n,&m);

    solve();
    }
    return 0;
}

  

原文地址:https://www.cnblogs.com/passion-sky/p/9065728.html