/*
Kattis - aplusb
题意大体是
给一些数,求满足i,j,k两两不相等且ai+aj=ak条件的(i,j,k)个数。
大体思路是
先把数存入一个数组
然后求出ai+aj的所有可能的值的个数
记录下每种数的个数直接和自己FFT就可求出
每种数有几种方式得来
然后把所有数组中的数对应的个数相加就是答案。
注意:
1.为了方便之后的运算,把负数先变成整数,即所有数加50000,因为最小数是-50000
2.FFT直接求出的是包括i==j的个数,求出后需要把i==j的情况减掉。
3.有可能某个数为0,为0会导致FFT算出的结果有,i==k或者j==k亦或者i==j==k的情况.
具体有如下三种情况
0+ai=ai,ai+0=ai,0+0=0。
如果是前两种情况,
即:0+ai=ai,ai+0=ai,
直接减掉两倍的0的个数就好。
如果是第三种情况,
即:0+0=0
减掉两倍(0的个数-1)即可,
-1是为了减去其中一个0变成ai,即上述两种情况的一种
两倍是因为,加法有交换律,0+0=0这种情况代码会算两遍
*/
#include <stdio.h>
#include <iostream>
#include <cmath>
#include <cstring>
#define LL long long
using namespace std;
const double PI=acos(-1.0);
const int MAXN = 2e5;
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[2*MAXN+5], x2[2*MAXN+5], x[2*MAXN+5];//x1和x2是相乘的两个多项式,x是得到的多项式
void change(complex *y, int len)//fft模板
{
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)//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], 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;
}
const int T = 50000;
LL num[MAXN+5];
LL cnt[MAXN+5];
LL ans[2*MAXN+5];
int main()
{
int n;
scanf("%d",&n);//输入n
memset(cnt,0,sizeof(cnt));
int zero=0;//有多少个0
for(int i=0; i<n; i++)
{
scanf("%lld",&num[i]);//输入一组数
if(num[i]==0)//判断这组数有多少个0
zero++;
cnt[num[i]+T]++;//使输入的这个数一定是正数,记录每个数出现了几次
}
int len = 1;//fft模板
while(len<MAXN)//fft模板
len<<=1;
for(int i=0; i<MAXN; i++)//fft初始化x1,a为多项式系数,b为0
x1[i].a = 1.0*cnt[i], x1[i].b = 0;
for(int i=0; i<MAXN; i++)//fft初始化x2,a为另一个多项式系数,b为0,这里两个多项式一样
x2[i].a = 1.0*cnt[i], x2[i].b = 0;
fft(x1, len, 1), fft(x2, len, 1);//fft模板
for(int i=0; i<len; i++)//fft模板
x[i] = x1[i] * x2[i];
fft(x, len, -1);//fft模板
memset(ans, 0, sizeof(ans));
for(int i=0; i<len; i++)//四舍五入,将算出来的多项式的系数存入另一个数组,fft模板
ans[i] = (LL)(x[i].a+0.5);//将ans全部加起来就是存在i==j,j==k,i==k,i==j==k的总情况数
//以下为这道题非模板部分
for(int i=0; i<n; i++)//减去i==j的情况
{
ans[2*(num[i]+T)]--;
}
LL res = 0;
for(int i=0; i<n; i++)//对于每种情况求和,再减去有0的情况
{
res+=ans[num[i]+T*2];
res-=(zero-(num[i]==0))*2;
}
printf("%lld\n",res);
return 0;
}
Kattis - aplusb
题意大体是
给一些数,求满足i,j,k两两不相等且ai+aj=ak条件的(i,j,k)个数。
大体思路是
先把数存入一个数组
然后求出ai+aj的所有可能的值的个数
记录下每种数的个数直接和自己FFT就可求出
每种数有几种方式得来
然后把所有数组中的数对应的个数相加就是答案。
注意:
1.为了方便之后的运算,把负数先变成整数,即所有数加50000,因为最小数是-50000
2.FFT直接求出的是包括i==j的个数,求出后需要把i==j的情况减掉。
3.有可能某个数为0,为0会导致FFT算出的结果有,i==k或者j==k亦或者i==j==k的情况.
具体有如下三种情况
0+ai=ai,ai+0=ai,0+0=0。
如果是前两种情况,
即:0+ai=ai,ai+0=ai,
直接减掉两倍的0的个数就好。
如果是第三种情况,
即:0+0=0
减掉两倍(0的个数-1)即可,
-1是为了减去其中一个0变成ai,即上述两种情况的一种
两倍是因为,加法有交换律,0+0=0这种情况代码会算两遍
*/
#include <stdio.h>
#include <iostream>
#include <cmath>
#include <cstring>
#define LL long long
using namespace std;
const double PI=acos(-1.0);
const int MAXN = 2e5;
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[2*MAXN+5], x2[2*MAXN+5], x[2*MAXN+5];//x1和x2是相乘的两个多项式,x是得到的多项式
void change(complex *y, int len)//fft模板
{
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)//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], 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;
}
const int T = 50000;
LL num[MAXN+5];
LL cnt[MAXN+5];
LL ans[2*MAXN+5];
int main()
{
int n;
scanf("%d",&n);//输入n
memset(cnt,0,sizeof(cnt));
int zero=0;//有多少个0
for(int i=0; i<n; i++)
{
scanf("%lld",&num[i]);//输入一组数
if(num[i]==0)//判断这组数有多少个0
zero++;
cnt[num[i]+T]++;//使输入的这个数一定是正数,记录每个数出现了几次
}
int len = 1;//fft模板
while(len<MAXN)//fft模板
len<<=1;
for(int i=0; i<MAXN; i++)//fft初始化x1,a为多项式系数,b为0
x1[i].a = 1.0*cnt[i], x1[i].b = 0;
for(int i=0; i<MAXN; i++)//fft初始化x2,a为另一个多项式系数,b为0,这里两个多项式一样
x2[i].a = 1.0*cnt[i], x2[i].b = 0;
fft(x1, len, 1), fft(x2, len, 1);//fft模板
for(int i=0; i<len; i++)//fft模板
x[i] = x1[i] * x2[i];
fft(x, len, -1);//fft模板
memset(ans, 0, sizeof(ans));
for(int i=0; i<len; i++)//四舍五入,将算出来的多项式的系数存入另一个数组,fft模板
ans[i] = (LL)(x[i].a+0.5);//将ans全部加起来就是存在i==j,j==k,i==k,i==j==k的总情况数
//以下为这道题非模板部分
for(int i=0; i<n; i++)//减去i==j的情况
{
ans[2*(num[i]+T)]--;
}
LL res = 0;
for(int i=0; i<n; i++)//对于每种情况求和,再减去有0的情况
{
res+=ans[num[i]+T*2];
res-=(zero-(num[i]==0))*2;
}
printf("%lld\n",res);
return 0;
}