hiho一下 第263周 小Hi和小Ho的礼物描述 hiho1505

题意:

从N(N<=1000)个数里找四元组(i, j, p, q)满足i, j, p, q两两不同,并且i < j, p < q, Ai + Aj = Ap + Aq的数量。

思路:

计数 + 容斥原理:

从N的范围,这题的解法必须是O(n^2) 附近的解法。我们可以思考遍历i,j时候,能不能O(1)知道A[i]+A[j]=x的对数呢?
是可以的,预处理出num[A[i]],sum[A[i]+A[j]] 表示:A[i] 出现次数,A[i]+A[j] 和出现的次数。

考虑遍历到 i,j,A[i] + A[j] = x 时:

由容斥原理:能找到在坐标i,j 之后 和为 x 的组合对数 = num[A[i]+A[j]] (和为x总次数,包括A[i],A[j]的组合) - 出现过A[i]或者A[j]的和为x的组合数。

而出现过A[i]或者A[j]的和为x的组合数,有两种思路:

第一种直接:

A[i]!=A[j] , 如果现在i,j时红线,下标1,3。还可能出现包含A[i]的 A[i] +A[k] =x对数数量一定是 num[A[j]]。 如图,下标1的1除了红线一条还有两条浅蓝色。同理,包含A[j]的A[k]+A[j]=x的对数数量是num[A[i]],这样计算红色线即包含A[i]又包含A[j]的被算了两次,所以简1。
ans =sum[A[i]+A[j]] -( num[A[i]] + num[A[j]]-1)
在这里插入图片描述
当**A[i] = A[j]**时候,包含A[i]+A[k]和A[k]+A[j]的数量都需要减一。

ans =sum[A[i]+A[j]] -(( num[A[i]]-1) + (num[A[j]]-1)-1)

第二种思路,这里再用容斥原理思想:
当A[i] != A[j] : 已知出现A[i],A[j]次数为num[i],num[j],则总共和为X的不同组合数为num[A[i]]*num[A[j]],去掉A[i],A[j],还有(num[A[i]]-1)*(num[A[j]] - 1),则包括A[i],A[j]组合数为 num[A[i]]*num[A[j]] - (num[A[i]]-1)*(num[A[j]]-1)

当A[i] == A[j]: 已知出现A[i],A[j]次数为num[i],num[j],则总共和为X的不同组合数为num[A[i]]*(num[A[j]]-1)/2,去掉A[i],A[j],还有(num[A[i]]-2)*(num[A[j]] - 3)/2。

当然,这两种思路化简后公式是一样的,详见代码。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1010;
map<ll, int> num,sum;
int A[N];

int main(){
    int n;
    while(~scanf("%d",&n)){
        num.clear(); sum.clear();
        for(int i=1;i<=n;i++){
           scanf("%d",&A[i]);
           num[A[i]]+=1;
        }

        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                sum[A[i]+A[j]]+=1;
            }
        }

        ll cnt = 0;
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                 //if(A[i] == A[j]) cnt += sum[A[i]+A[j]] - (num[A[i]] * (num[A[j]]-1)/2 - (num[A[i]]-2) *(num[A[j]]-3)/2);
                 //else cnt += sum[A[i]+A[j]] - (num[A[i]] * num[A[j]] - (num[A[i]]-1) *(num[A[j]]-1));
                 if(A[i] == A[j]) cnt += sum[A[i]+A[j]] - (num[A[i]] + num[A[j]]-3);
                 else cnt += sum[A[i]+A[j]] - (num[A[i]] + num[A[j]]-1);
            }
        }
        printf("%lld\n", cnt);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值