Kattis - aplusb(FFT)

本文介绍了一种解决Kattis平台上AplusB问题的有效算法。该问题要求找出所有满足特定条件的三元组(i, j, k),通过使用FFT快速傅里叶变换来高效计算组合数量,并特别处理了零元素的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
    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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值