POJ 3685 Matrix 二分套二分 POJ 3685 Matrix 二分

题意

有一个N阶方阵,方正中第i行第j列的元素值为(d_{i,j}=i^{2}+1e5*i+j^{2}-1e5*j+i*j),我们需要找出这个方阵中第M小的元素值。

解题思路

分析这个公式,我们发现:当j固定的时候,这个公式关于i(取值范围:从0n)是单调增加的,所以这里我们可以二分一个答案,然后一列一列的找小于(等于)它的个数,这样加起来我们就能知道我们枚举的这个答案是第几小了。

需要注意的是,第一个最外层的二分有点不同,因为我们二分的答案可能不存在,但是也是符合第m小,这个情况还是需要注意的,这里需要参看关于二分的第四种形式。二分链接

代码实现

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<stack>
#include<queue>
#include<map>
typedef long long ll;
using namespace std;
const double esp=1e-6;
const int inf=0x3f3f3f3f;
const int MAXN=1E6+7;
ll n, m;
ll fun(ll i, ll j)
{
	return i*i+100000*i+j*j-100000*j+i*j;
}
void dis()
{
	for(int i=1; i<=n; i++)
	{
		for(int j=1; j<=n; j++)
		{
			printf("%lld ", fun(i, j));
		}
		printf("
");
	}
}
ll solve(ll value)
{
	ll sum=0;
	for(ll j=1; j<=n; j++)
	{
		ll left=0, right=n+1, ans=0;
		while(left < right)
		{
			ll mid=left+(right-left)/2;
			if(fun(mid, j) <= value)
				left=mid+1;
			else right=mid;
		}
		if(left!=0)
			sum+=left-1;
	}
	return sum;
}
int main()
{
	int t;
	scanf("%d", &t);
	while(t--)
	{
		scanf("%lld%lld", &n, &m);
//		dis();
		ll lt=-1e12, rt=1e12, ans;
		while(lt+1 < rt) //这里的二分我们提前是不知道该向左侧还是右侧前进的。
		{
			ll mid = lt + (rt-lt)/2;
			ll tmp=solve(mid);
			if(tmp >= m)
			{
				ans=mid;
				rt=mid;
			}
			else lt=mid;
		}
		printf("%lld
", ans);	
	}
	return 0;
}