您的位置:首页 > 理论基础 > 计算机网络

2016 acm香港网络赛 A题. A+B Problem (FFT)

2016-09-10 21:00 363 查看
原题地址:https://open.kattis.com/problems/aplusb

FFT代码参考kuangbin的博客:http://www.cnblogs.com/kuangbin/archive/2013/07/24/3210565.html

A+B Problem

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

Author(s): Tung Kam Chuen

Source: Hong Kong Regional Online Preliminary 2016

题意:给一个数列,从中选三个数 ai, aj, ak,使得ai+aj=ak,问共有多少组( i, j, k)满足条件。

其实就是FFT。

注意一下a数组的数据范围,a[i]可能为负数,所有a[i]+50000,把负数转化为正数处理;

如果数组里有0,先把0删掉,最后特殊处理。

把a数组转化成num数组,num[i]表示i出现的次数。

然后num数组和num数组卷积,得到新的num数组。

num数组意义:num[x]表示使ai+aj=x,(i,j)的取法有多少种。

#include <algorithm>
#include <cstring>
#include <string.h>
#include <iostream>
#include <list>
#include <map>
#include <set>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#include <cstdio>
#include <cmath>

#define LL long long
#define N 200005
#define INF 0x3ffffff

using namespace std;

const double PI = acos(-1.0);

struct Complex // 复数
{
double r,i;
Complex(double _r = 0,double _i = 0)
{
r = _r; i = _i;
}
Complex operator +(const Complex &b)
{
return Complex(r+b.r,i+b.i);
}
Complex operator -(const Complex &b)
{
return Complex(r-b.r,i-b.i);
}
Complex operator *(const Complex &b)
{
return Complex(r*b.r-i*b.i,r*b.i+i*b.r);
}
};

void change(Complex y[],int len) // 二进制平摊反转置换 O(logn)
{
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 /= 2;
}
if(j < k)j += k;
}
}
void fft(Complex y[],int len,int on) //DFT和FFT
{
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];
Complex 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].r /= len;
}

const int M =50000;          // a数组所有元素+M,使a[i]>=0
const int MAXN = 800040;

Complex x1[MAXN];
int a[MAXN/4];                //原数组
long long num[MAXN];     //利用FFT得到的数组
long long tt[MAXN];        //统计数组每个元素出现个数

int main()
{
int n=0;                   // n表示除了0之外数组元素个数
int tot;
scanf("%d",&tot);
memset(num,0,sizeof(num));
memset(tt,0,sizeof(tt));

int cnt0=0;             //cnt0 统计0的个数
int aa;

for(int i = 0;i < tot;i++)
{
scanf("%d",&aa);
if(aa==0) {cnt0++;continue;}  //先把0全删掉,最后特殊考虑0
else a
=aa;
num[a
+M]++;
tt[a
+M]++;
n++;
}

sort(a,a+n);
int len1 = a[n-1]+M+1;
int len = 1;

while( len < 2*len1 ) len <<= 1;

for(int i = 0;i < len1;i++){
x1[i] = Complex(num[i],0);
}
for(int i = len1;i < len;i++){
x1[i] =Complex(0,0);
}
fft(x1,len,1);

for(int i = 0;i < len;i++){
x1[i] = x1[i]*x1[i];
}
fft(x1,len,-1);

for(int i = 0;i < len;i++){
num[i] = (long long)(x1[i].r+0.5);
}

len = 2*(a[n-1]+M);

for(int i = 0;i < n;i++) //删掉ai+ai的情况
num[a[i]+a[i]+2*M]--;
/*
for(int i = 0;i < len;i++){
if(num[i]) cout<<i-2*M<<' '<<num[i]<<endl;
}
*/
long long ret= 0;

int l=a[n-1]+M;

for(int i = 0;i <=l; i++)   //ai,aj,ak都不为0的情况
{
if(tt[i]) ret+=(long long)(num[i+M]*tt[i]);
}

ret+=(long long)(num[2*M]*cnt0);        // ai+aj=0的情况

if(cnt0!=0)
{
if(cnt0>=3) {                   //ai,aj,ak都为0的情况
long long tmp=1;
tmp*=(long long)(cnt0);
tmp*=(long long)(cnt0-1);
tmp*=(long long)(cnt0-2);
ret+=tmp;
}
for(int i = 0;i <=l; i++)
{
if(tt[i]>=2){             // x+0=x的情况
long long tmp=(long long)cnt0;
tmp*=(long long)(tt[i]);
tmp*=(long long)(tt[i]-1);
ret+=tmp*2;
}
}
}

printf("%lld\n",ret);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: