题意
对于一组有m个数,如果要取两个数a和b,使得这两个数的和大于k,那么可以将这组数由小到大排序,然后枚举a,查找满足条件的数b的个数,在查找数b的个数时,可以利用lower_bound函数(二分查找)。比如:我们找到第一个满足a+b>k的b是第i个数,则第i+1,i+2,……一直到最后一个数都满足。枚举a的过程记数并加和,得到的结果就是满足条件的(a,b)对的2倍。(因为你每个人都要加一遍,所有人实际上都重复加了一次,给小学数学中,握手问题差不多,每个人都去给所有人握手,然后,每个人都会给同一个人握两次手),所以答案要除以2;
题目要求不同的集合,可以利用总集合中满足条件的数对 - 相同集合满足条件的集合数对。
(注意:单个集合的数最多有100,集合数目最多有1000,则总集合的数最多有100000)
下面代码详细解释
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std;
typedef long long ll;
struct IQ {
ll m;
ll v[100005];
}a[1005];
int main()
{
ll i,j,l,m,n,v,k,t;
scanf("%I64d",&t);
while(t--){
scanf("%I64d%I64d",&n,&k);
a[0].m=0;//标记总人数
for( i=1,l=0;i<=n;i++)
{
scanf("%I64d",&m);
a[i].m=m;//第几个班有多少人
a[0].m+=m;//共有多少人存放在a[0].m中
for(j=0;j<m;j++){
scanf("%I64d",&v);
a[i].v[j]=v;//i班每个同学的成绩,存放在数组里
a[0].v[l++]=v; //将每个同学的成绩都存在a[0].v[]数组内
}
sort(a[i].v,a[i].v+m);//将本班同学的成绩排序
}
sort(a[0].v,a[0].v+a[0].m); //将所有同学成绩排序
ll ans=0;
for(i=1;i<=n;i++)
{
for(j=0;j<a[i].m;j++)
{
v=a[i].v[j];
ll x=lower_bound(a[0].v,a[0].v+a[0].m,k-v+1)-a[0].v;//(k-v+1)Dudu智商是k,然后这个人的智商是v,找到比k大的数.a[0].v是数组的起始位置
ll n1=a[0].m-x;
ll y=lower_bound(a[i].v,a[i].v+a[i].m,k-v+1)-a[i].v;//同上
ll n2=a[i].m-y;
ans+=n1-n2;
}
}
printf("%I64d\n",ans/2);
}
return 0;
}