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