题意 给你 n 阶一个矩阵 Aij = i*i + i*100000 - j*100000 +i*j
求第 m 大的数字
先输入 T 之后 T 组数据, 每次输入 n ,m。
首先看一下范围 发现超不了 LL,之后找一下规律,发现 i 和 Aij 是正相关的,i 大则 Aij 也会大,但是似乎并没有找到什么规律,这道题更多的是找到 一个数字对吧 找到第 K 大的数,代表有个 K-1个数字 比 它大,而 i 根 Aij 是正相关, 所以可以尝遍历 ai j j (1 -> n) 但是这样做可以优化, 暴力搜索总是不如二分搜索来的快 ,但是我们如果二分,排序的问题又有点费力,我们只知道二分一下 这个数是什么,并不知道它排第几位对吧,不得不说 之后一步我看了一下大犇们的博客,发现确实脑洞大开,我们不如从整个数字范围开始二分,找一个数,看一下他能不能在 Aij 之中排到我们想要的位置,这一步确实看得我很惊讶。。。。
总结一下:
在数据范围内选一个数,看一下他在 A矩阵中 有几个比它更大的数,通过二分搜索,每次出现一个比它大的 就计数,知道简单遍历完毕整个 Aij (如果 遍历 i 而二分 j 那么效率是 O(n*logn)可以接受) 之后至于我们如何找到这个数 就是通过整个实属范围内大型二分搜索(真的让人受益匪浅。。没想到二分可以这么用)
以下为 AC代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long int LL;
LL n,m;
LL cal(LL i,LL j)
{
return i*i+j*j+i*j+100000*i-100000*j;
}
LL findd(LL x)
{
LL num=0;
LL l,r,mid,i;
for(i=1;i<=n;i++)
{
l=1,r=n+1;
mid=(l+r)>>1;
while(l<r)
{
if(cal(mid,i)>=x)
r=mid;
else
l=mid+1;
mid=(l+r)>>1;
}
num += mid-1;
}
return num;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld",&n,&m);
LL l,r,mid;
l=-1e12,r=1e12;
mid = (l+r)>>1;
while(l<r)
{
if(findd(mid)>=m)r=mid;
else l=mid+1;
mid = (l+r)>>1;
}
printf("%lld\n",mid-1);
}
return 0;
}