Kattis aplusb A+B Problem(FFT)

A+B Problem
Given NN integers in the range [−50000,50000], how many ways are there to pick three integers aiai, ajaj, akak, such that ii, jj, kk are pairwise distinct and ai+aj=akai+aj=ak? Two ways are different if their ordered triples (i,j,k)(i,j,k) of indices are different.

Input
The first line of input consists of a single integer NN (1≤N≤200000). The next line consists of NN space-separated integers a1,a2,…,aNa1,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个数中任取三个组合成 a i + a j = a k a_{i}+a_{j}=a_{k} ai+aj=ak对数

思路

这题和HDU4609一样,做法也一样,但是因为数据范围里会出现负数所以我们要给每个数加上一个M=50000
一样的做法,把给的数作为多项式的次幂,次数为系数,然后用FFT进行多项式的乘法

注意1
这题(i,j)和(j,i)认为不同的,所以答案不需要除二,但是自己取自己的情况不包括在内所以还要历遍数组减去两倍的每一个数
注意2
可能存在给的数为0的情况,当你给的数为0是就会有 0 + 0 = 0 0+0=0 0+0=0的情况和 a i + 0 = a i a_{i}+0=a_{i} ai+0=ai的情况,因为自己取自己是非法的,所以这里要删去一些情况
我们先统计一下0的个数为z个,那么就有
2 z ( n − z ) + 2 z ( z − 1 ) = 2 z ( n − 1 ) 2z(n-z)+2z(z-1)=2z(n-1) 2z(nz)+2z(z1)=2z(n1)
所以最后的答案再减去 2 z ( n − 1 ) 2z(n-1) 2z(n1)就好了
注意3
因为每个数都加上了M
所以那个题意中的式子应为 a i + M + a j + M = a k + M + M a_{i}+M+a_{j}+M=a_{k}+M+M ai+M+aj+M=ak+M+M

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <math.h>
#include <stack>
#define PI acos(-1)
using namespace std;
typedef long long ll;
const int MAXN = 1e6+5;
struct complex {
    double a, b;
    complex(double aa = 0.0, double bb = 0.0) { a = aa; b = bb; }
    complex operator +(const complex &e) { return complex(a + e.a, b + e.b); }
    complex operator -(const complex &e) { return complex(a - e.a, b - e.b); }
    complex operator *(const complex &e) { return complex(a * e.a - b * e.b, a * e.b + b * e.a); }
}x1[MAXN], x2[MAXN], x[MAXN];
void change(complex *y, int len){
    int i, j, k;
    for(i=1,j=len/2; i<len-1; i++){
        if(i < j) swap(y[i], y[j]);
        k = len/2;
        while(j >= k){ j-=k; k>>=1; }
        if(j < k) j += k;
    }
}
void fft(complex *y, int len, int on){
    change(y, len);
    for(int h=2; h<=len; h<<=1){
        complex wn(cos(-on*2*PI/h), sin(-on*2*PI/h));
        for(int j=0; j<len; j+=h){
            complex w(1, 0);
            for(int k=j; k<j+h/2; k++){
                complex u = y[k], t = w*y[k+h/2];
                y[k] = u + t, y[k+h/2] = u- t, w = w*wn;
            }
        }
    }
    if(on == -1) for(int i=0; i<len; i++)  y[i].a /= len;
}
long long a[MAXN];
long long ans[MAXN];
long long M=50000;
int main()
{
    long long n;
    scanf("%lld",&n);
        memset(x1,0,sizeof(x1));
        memset(ans,0,sizeof(ans));
        long long maxn=0;
        long long zero=0;
        for(int i=0;i<n;i++)
        {
            scanf("%lld",&a[i]);
            if(!a[i]) zero++;
            x1[a[i]+M].a++;
            maxn=max(a[i]+M,maxn);
        }
        int len=1;
        while(len<2*maxn) len<<=1;
        fft(x1,len,1);
        memset(x,0,sizeof(x));
        for(int i=0;i<len;i++)
            x[i]=x1[i]*x1[i];
        fft(x,len,-1);
        for(int i=0;i<len;i++)
            ans[i]=(ll)(x[i].a+0.5);
        for(int i=0;i<n;i++)
            ans[2*(a[i]+M)]--;
        ll sum=0;
        for(int i=0;i<n;i++)
            sum+=ans[a[i]+M+M];
        sum-=2*zero*(n-1);
        printf("%lld\n",sum);
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值