HDU-5101,二分算法

题意

对于一组有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;
}

猜你喜欢

转载自blog.csdn.net/Harington/article/details/82011585