POJ2785 4 Values whose Sum is 0(折半枚举+二分)

题目大意

给定A B C D四个数组,每个数组里面有n(n<=40000)个数

问从四个数组里各选一个数,相加等于零的情况有多少种

(如果两个数值相同而位置不同算同一种情况)

思路

如果是四重循环找,不用说绝对超时

那么就需要用到一些优化了

其实我们可以先将C和D的情况先枚举出来(两重循环)并对其排序

然后再用两重循环将A和B的和的情况取相反数

再用upper_bound和lower_bound找到在有序数组内有没有相等的数,累加答案

(upper_bound找到大于的数,lower_bound找到大于等于的数,相减就是结果)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
ll A[4005],B[4005],C[4005],D[4005];
ll fcd[16000005],ans,s;
int n,len;
int main()
{
    ans=0;len=0;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    scanf("%lld%lld%lld%lld",&A[i],&B[i],&C[i],&D[i]);

    for(int i=1;i<=n;++i)
     for(int j=1;j<=n;++j)
      fcd[++len]=C[i]+D[j];//枚举C和D的所有方案

    sort(fcd+1,fcd+len+1);//lower_bound和upper_bound一定要有序

    for(int i=1;i<=n;++i)
     for(int j=1;j<=n;++j)
     {
         s=-(A[i]+B[j]);//记得是早相反数
         ans+=upper_bound(fcd+1,fcd+len+1,s)-lower_bound(fcd+1,fcd+len+1,s);
     }

    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值