Kattis - aplusb A+B Problem 2016年ACM香港网络赛 FFT

https://open.kattis.com/problems/aplusb
Given N integers in the range [−50000,50000], how many ways are there to pick three integers ai, aj, ak, such that i, j, k are pairwise distinct and ai+aj=ak? Two ways are different if their ordered triples (i,j,k) of indices are different.

Input
The first line of input consists of a single integer N (1≤N≤200000). The next line consists of N space-separated integers a1,a2,…,aN.

Output
Output an integer representing the number of ways.

Sample Input 1 Sample Output 1
4
1 2 3 4
4
Sample Input 2 Sample Output 2
6
1 1 3 3 4 6
10

题目大意:给 n n n个范围在 [ − 50000 , 50000 ] [-50000,50000] [50000,50000]的数,让你求满足 a i + a j = a k a_i+a_j=a_k ai+aj=ak的三元组 ( i , j , k ) (i,j,k) (i,j,k)的数目,且 i 、 j 、 k i、j、k ijk互不相同。
思路: F F T FFT FFT,个人感觉很难的一道题orz……看了半天题解才看懂。
题解:https://blog.csdn.net/Floraqiu/article/details/86611210

#include<bits/stdc++.h>
using namespace std;        //FFT模板
typedef long long ll;

const int maxn=1e5+5;

struct Complex
{
    double x,y;
    Complex(double dx=0,double dy=0)
    {
        x=dx;
        y=dy;
    }
};

Complex operator +(Complex a,Complex b)
{
    return Complex(a.x+b.x,a.y+b.y);
}
Complex operator -(Complex a,Complex b)
{
    return Complex(a.x-b.x,a.y-b.y);
}
Complex operator *(Complex a,Complex b)
{
    return Complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
}

const double pi=acos(-1.0); //PI
int limit=1,bit=0;
int wz[maxn<<2];
Complex a[maxn<<2];
int v[maxn<<2],cnt[maxn<<2];
ll num[maxn<<2];

void FFT(Complex *A,int inv)
{
    for(int i=0;i<limit;i++)
        if(i<wz[i])
            swap(A[i],A[wz[i]]);
    for(int mid=1;mid<limit;mid<<=1)
    {
        Complex wn(cos(pi/mid),inv*sin(pi/mid));
        for(int i=0;i<limit;i+=mid<<1)
        {
            Complex w(1,0);
            for(int j=0;j<mid;j++,w=w*wn)
            {
                Complex t1=A[i+j];
                Complex t2=w*A[i+mid+j];
                A[i+j]=t1+t2;
                A[i+mid+j]=t1-t2;
            }
        }
    }
}

int main()
{
    int n,zero=0;
    while(~scanf("%d",&n))
    {
        memset(wz,0,sizeof(wz));
        memset(a,0,sizeof(a));
        memset(cnt,0,sizeof(cnt));
        memset(num,0,sizeof(num));
        limit=1,bit=0;
        int len=-1;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&v[i]);
            cnt[v[i]+50000]++;
            len=max(len,v[i]+50000);
            if(v[i]==0)
                ++zero;
        }
        int tmp=len;
        len<<=1;
        while(limit<=len)
        {
            limit<<=1;
            bit++;
        }
        for(int i=0;i<limit;i++) //从低位到高位
        {
            if(i<=tmp)
                a[i].x=cnt[i];
            else
                a[i].x=0;
        }
        for(int i=0;i<limit;i++)
            wz[i]=(wz[i>>1]>>1)|((i&1)<<(bit-1));
        FFT(a,1);
        for(int i=0;i<limit;i++)
            a[i]=a[i]*a[i];
        FFT(a,-1);
        for(int i=0;i<limit;i++)
            num[i]=(ll)(a[i].x/limit+0.5);
        for(int i=0;i<n;i++)
            num[(v[i]+50000)*2]--;
        ll ans=0;
        for(int i=0;i<n;i++)
        {
            ans+=num[v[i]+100000];
            ans-=2*zero;
            if(v[i]==0)
                ans+=2;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值