1 3 1 1 2 1 2 2 1 1
5
题目大意:给出n个集合,每个集合有m个数,不同的集合,m的值不一定相同,问从不同的集合中取两个数,使得这两个数的和大于k的取法有多少种。
解题思路:
对于一组有m个数,如果要取两个数a和b,使得这两个数的和大于k,那么可以将这组数由小到大排序,然后枚举a,查找满足条件的数b的个数,在查找数b的个数时,可以利用lower_bound函数(二分查找)。比如:我们找到第一个满足a+b>k的b是第i个数,则第i+1,i+2,……个数都满足。枚举a的过程记数并加和,得到的结果就是满足条件的(a,b)对的2倍。
题目要求不同的集合,可以利用总集合中满足条件的数对 - 相同集合满足条件的集合数对。
(注意:单个集合的数最多有100,集合数目最多有1000,则总集合的数最多有100000)
代码如下:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#include <limits.h>
#define debug "output for debug\n"
#define pi (acos(-1.0))
#define eps (1e-6)
#define inf (1<<28)
#define sqr(x) (x) * (x)
#define mod 1000000007
using namespace std;
typedef long long ll;
typedef unsigned long long ULL;
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;
for(j=0;j<m;j++)
{
scanf("%I64d",&v);
a[i].v[j]=v;
a[0].v[l++]=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;
ll n1=a[0].m-x;
//printf("i=%I64d j=%I64d x=%I64d n1=%I64d ",i,j,x,n1);
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;
//printf("i=%I64d j=%I64d y=%I64d n2=%I64d\n",i,j,y,n2);
ans+=n1-n2;
}
}
printf("%I64d\n",ans/2);
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
HDU 5101 Select(不同集合的两个数大于k的方案数)
原文地址:http://blog.csdn.net/yanghuaqings/article/details/47749053