二分套二分_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; }