小Hi和小Ho的礼物 (哈希记和 + 容斥)

某人有N袋金币,其中第i袋内金币的数量是Ai。现在他决定选出2袋金币送给小Hi,再选2袋金币送给小Ho,同时使得小Hi和小Ho得到的金币总数相等。他想知道一共有多少种不同的选择方法。

具体来说,有多少种下标四元组(i, j, p, q)满足i, j, p, q两两不同,并且i < j, p < q, Ai+ Aj = Ap + Aq。  

例如对于数组A=[1, 1, 2, 2, 2],一共有12种选法:

i j p q
1 3 2 4
1 3 2 5
1 4 2 3
1 4 2 5
1 5 2 3
1 5 2 4
2 3 1 4
2 3 1 5
2 4 1 3
2 4 1 5
2 5 1 3
2 5 1 4

Input

第一行包含一个整数N。  

第二行包含N个整数,A1, A2, A3 ... AN。

对于70%的数据,1 <= N <= 100  

对于100%的数据,1 <= N <= 1000, 1 <= Ai <= 1000000

Output

不同选择的数目。

Sample Input

5  
1 1 2 2 2

Sample Output

12

题意:中文题...

思路:因为每次都是拿出来两个,所以我们可以先求一下,每一次拿出来两袋的和,并记录这个和出现次数。然后我i们再枚举一次 i ,j ,此时我们就可以直接知道这个和出现的次数。但是里面肯定有很多重复的,所以我们要记录一下每个数出现的次数。然后分情况讨论

   1.num[i] ! = num[j]     eg:   1   1   2   2   2

            如果我们现在枚举的是 1,3号点(红线),算出和为3.但是我们第一次枚举的和为3的所有情况,记个数为 temp;

      又因为另外一组的不能拿1和3号点,所以我们要减去2的个数(蓝线),因为 1(此时 i 的位置上的值)+2 =3。我们再看一眼上图,2号点和3号点也相连了(紫虚线),所以我们还要把以二号点为终点的所有线给去掉,也就是再减去 1 的个数。然后我发现1->3 这条线减了两次,因此我们要加 +1. 。综合 t = v[sum] - u[ num[i] ]] - u[ num [j] ] + 1;

   2.  num[i] = = num[j]     eg:   1   1   1   1  1

       同理  t = v[sum] - u[ num[i] ]] - u[ num [j] ] + 3;

 

综上 :1. num[i] = = num[j]      t = v[sum] - u[ num[i] ]] - u[ num [j] ] + 3;

            2.num[i]  ! = num[j]      t = v[sum] - u[ num[i] ]] - u[ num [j] ] + 1;

代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<algorithm>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
map<ll,int>v,u;
ll num[10010];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0; i<n; i++)
    {
        scanf("%lld",&num[i]);
        u[num[i]]++;
    }
    sort(num,num+n);
    int q=1;
    for(int i=0; i<n; i++)
        for(int j=i+1; j<n; j++)
        {
            ll x=num[i]+num[j];
            v[x]++;
        }
    ll ans=0;
    for(int i=0; i<n; i++)
        for(int j=i+1; j<n; j++)
        {
            ll x=num[i]+num[j];
            ll t;
            if(num[i]==num[j])
                t=v[x]-u[num[j]]-u[num[i]]+3;
            else
                t=v[x]-u[num[j]]-u[num[i]]+1;
            if(t>0)ans+=t;
        }
    printf("%lld\n",ans);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值