题目地址:点击打开链接
题意:给你一个n*n矩阵,每一个位置都有一个值,这个值由该点的该点的行列标决定( i^2 + 100000 × i + j^2 - 100000 × j + i × j),问你第m小的元素是多少
思路:
二分答案,然后判断这个答案是在m之前还是之后。那么怎么知道这个二分的值在前在后呢?可以数比他小的数有多少,那怎么数呢?暴力n*n肯定超时,由给出的公式可知j确定时,表达式关于i单调,所以就可以枚举j二分i确定每个j比他小的数由多少个,这样就可以nlogn时间判断,最后复杂度n*logn*logn。
WA:
一开始找到的规律是从右上角开始以对角线形式递增,所以要求第几大,可以通过二分对角线,找到在哪条对角线然后找到行号列号就可以了。。但是不知道为什么wa了...
后来知道了是n到了5*10^4后这个规律就不一定存在了
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int C = 1e5;
const int maxn = 5e4+5;
ll n, m;
bool judge(ll x)
{
ll num = 0;
for(ll j = 1; j <= n; j++)
{
ll l = 1, r = n, ans = 0;
while(l <= r)
{
ll mid = (l+r)/2;
ll tmp = mid*mid+C*mid+j*j-C*j+j*mid;
if(tmp <= x) l = mid+1, ans = mid;
else r = mid-1;
}
num += ans;
if(num >= m) return 1;
}
return 0;
}
int main(void)
{
int t;
cin >> t;
while(t--)
{
scanf("%lld%lld", &n, &m);
ll ans, l = -C*n, r = n*n+C*n+n*n+n*n;
while(l <= r)
{
ll mid = (l+r)/2;
if(judge(mid)) ans = mid, r = mid-1;
else l = mid+1;
}
printf("%lld\n", ans);
}
return 0;
}
WA:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int C = 1e5;
const int maxn = 1e5+5;
ll a[maxn], n, m;
void init()
{
for(int i = 1; i <= n; i++)
a[i] = a[i-1]+i;
for(int i = n+1; i <= 2*n-1; i++)
a[i] = a[i-1]+n-(i-n);
}
int main(void)
{
int t;
cin >> t;
while(t--)
{
scanf("%lld%lld", &n, &m);
init();
ll tmp = lower_bound(a+1, a+2*n-1, m)-a, row, col;
if(m <= (1+n)*n/2)
col = n-(a[tmp]-m), row = tmp-(a[tmp]-m);
else
col = n-(tmp-n)-(a[tmp]-m), row = n-(a[tmp]-m);
// cout << tmp <<endl;
// cout << row << ' ' << col <<endl;
printf("%lld\n", row*row+C*row+col*col-C*col+row*col);
}
return 0;
}