題意
對於一組有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;
}