要求:n行数,每行有四个数,ai,bi,ci,di,求有多少种组合方式,使a+b+c+d=0,这四个数可以不在一行。n<=4000,a,b,c,d<=2^28。
方法:折半枚举。
这题本来两个二重循环和两个map准备很快解决掉,但是直接TLE,后来发现二分查找比map查找快。
1.用二重循环分别记录所有a+b和所有c+d的结果,然后将c+d的结果存起来并排序,扫a+b的值,二分搜索存储c+d的数组,有几个值是-a-b。
2.这其实是二分的策略,只是二分的答案是一个连续的子序列。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
using namespace std;
map<long long,long long>map1;
int num1;
long long ab[16000001],cd[16000001];
int main()
{
int n,i,j,k,p,q,cnt=0,l,r,mid,num,temp;
long long a[4001],b[4001],c[4001],d[4001];
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%lld%lld%lld%lld",&a[i],&b[i],&c[i],&d[i]);
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
ab[cnt]=a[i]+b[j];
cd[cnt]=c[i]+d[j];
cnt++;
}
}
sort(cd,cd+cnt);
num=0;
for(i=0;i<cnt;i++)
{
l=0,r=cnt-1,mid=(l+r)/2;
while(cd[mid]!=(-ab[i])&&l<=r)
{
mid=(l+r)/2;
if(cd[mid]>(-ab[i])) r=mid-1;
else if(cd[mid]<(-ab[i])) l=mid+1;
}
if(cd[mid]!=(-ab[i])) continue;
num++;
temp=mid;
while(temp-1>=0&&cd[--temp]==(-ab[i])) num++;
temp=mid;
while(temp+1<cnt&&cd[++temp]==(-ab[i])) num++;
}
printf("%d\n",num);
}