xdu1068暨2013陕西省赛C题题解
xdu1068暨2013陕西省赛C题题解
题意
知道两个数列M和F,每次从M中选择一个人,和从F中选择的一个人配对,结果是Mi*Fj,请问所有配对情况中第k大的情况是多少。
笺释
先对M和F从小到大排序,显然最小的情况是F[1]*M[1],最大的情况是F[n]*M[m],在这个区间内二分。
确定答案x时,若x使得所有配对情况中大于x的数目大于等于K的话,就意味着x太小啦,我们就将mid向右移来增大x,否则减小x。
确定x使得所有配对情况中大于x的数目大于等于k的还是小于k,用到了很厉害的一个性质,可以先回忆以下xdu第一周训练题目B题,那个题我们从小到大枚举数字,最小的数字可以与其余数字组成n-1个情况使得答案为最小的数字,但是这个题因为答案是两数乘积,所以不能单纯地由某一个数字确定,我们同样是从小到大枚举M中的数字Mi,然后F中的数字从大到小枚举Fj,这样就造成了:若j之前的满足Mi*Fj大于x,那么当i增大的时候,Mi增大,j之前的一定也满足,就不需要再枚举一遍,这样就将复杂度降低为了n+m而不是n*m。
具体说感觉也说不太清楚,结合代码理解吧。
完整代码
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
int t,A,B;
long long M[10005],F[10005];
int K;
int check(long long x)
{
int ret=0;
int j=B;
for(int i=1;i<=A;i++)
{
for(;(j>=1&&(M[i]*F[j])>x);j--);
ret+=B-j;
}
return ret;
}
ll solve()
{
ll l=M[1]*F[1];
ll r=M[A]*F[B];
while(l<=r)
{
ll mid=(l+r)/2;
//printf("A %lld %lld\n",l,r);
int tem=check(mid);
// printf("B %lld\n",tem);
if(tem>=K)
{
l=mid+1;
}
else
{
r=mid-1;
}
}
return r+1;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d %d %d",&A,&B,&K);
for(int i=1;i<=A;i++)
{
scanf("%lld",&M[i]);
}
for(int i=1;i<=B;i++)
{
scanf("%lld",&F[i]);
}
sort(M+1,M+1+A);
sort(F+1,F+1+B);
printf("%lld\n",solve());
}
}
少女が見た日本の原風景