日常学习——FFT

FFT相关详解:

http://blog.miskcoo.com/2015/04/polynomial-multiplication-and-fast-fourier-transform

https://blog.csdn.net/ggn_2015/article/details/68922404

练习题:

bzoj2179 FFT快速傅立叶:

模板题

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define inf 0x7f7f7f7f
using namespace std;
const int maxn=2e5;
const double pi=acos(-1);
int n,m,l;
int sum[maxn*2+10],rev[maxn+10];

int read()
{
    char ch;int x=0,f=1;
    for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}

struct complex
{
    double x,y;
    complex(){};
    complex(double _x,double _y){x=_x,y=_y;}
    complex operator +(const complex &a){return complex(x+a.x,y+a.y);}
    complex operator -(const complex &a){return complex(x-a.x,y-a.y);}
    complex operator *(const complex &a){return complex(x*a.x-y*a.y,x*a.y+y*a.x);}
}A[maxn+10],B[maxn+10],Ans[maxn*2+10];

int get()
{
    int ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar());
    return ch-'0';
}

void fft(complex *a,int len,int flag)
{
    for (int i=0;i<len;i++) if (rev[i]<i) swap(a[i],a[rev[i]]);
    for (int i=2;i<=len;i<<=1)
    {
        complex wn(cos(2*pi/i),sin(2*pi/i*flag));
        for (int j=0;j<len;j+=i)
        {
            complex nw(1,0);
            for (int k=0;k<(i>>1);k++,nw=nw*wn)
            {
                complex x=a[j+k],y=nw*a[j+k+(i>>1)];
                a[j+k]=x+y,a[j+k+(i>>1)]=x-y;
            }
        }
    }
    if (flag==-1) for (int i=0;i<len;i++) a[i].x/=len;
}

int main()
{
    n=read();
    for (int i=n-1;~i;i--) A[i].x=get();
    for (int i=n-1;~i;i--) B[i].x=get();
    for (n*=2,m=1;m<=n;m<<=1)l++; 
    for (int i=0;i<m;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
    fft(A,m,1),fft(B,m,1);
    for (int i=0;i<m;i++) Ans[i]=A[i]*B[i];
    fft(Ans,m,-1);
    for (int i=0;i<m;i++) sum[i]=(int)(Ans[i].x+0.5);
    for (int i=0;i<m;i++)
    {
        sum[i+1]+=sum[i]/10;
        sum[i]%=10;
    }
    while((!sum[n-1])&&n) n--;
    while(sum[n]) n++;
    for (int i=n-1;~i;i--) putchar(sum[i]+'0');
    printf("\n");
    return 0;
}

bzoj2194 快速傅立叶之二:

模板题+1。计算\(C_k=\sum_{i=k}^{n-1}{a_i \times b_{i-k}}\),将\(b[]\)翻转,则\(C_k=\sum_{i=k}^{n-1}{a_i \times b_{n+1+k-i}}\),即\(C_k=\sum\limits_{i+j=n+1+k,0<=i,j<=2 \times n}{a_i \times b_j}\)。直接FFT即可。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define inf 0x7f7f7f7f
using namespace std;
const int maxn=4e5;
const double pi=acos(-1);
int n,m,l;
int rev[(maxn<<2)+5];

int read()
{
    char ch;int x=0,f=1;
    for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}

struct complex
{
    double x,y;
    complex(){};
    complex(double _x,double _y){x=_x,y=_y;}
    complex operator +(const complex &a){return complex(x+a.x,y+a.y);};
    complex operator -(const complex &a){return complex(x-a.x,y-a.y);};
    complex operator *(const complex &a){return complex(x*a.x-y*a.y,x*a.y+y*a.x);};
}a[maxn+10],b[maxn+10],ans[(maxn<<2)+5];

void fft(complex *a,int len,int flag)
{
    for (int i=0;i<len;i++) if (rev[i]<i) swap(a[i],a[rev[i]]);
    for (int i=2;i<=len;i<<=1)
    {
        complex wn(cos(2*pi/i),sin(2*pi/i*flag));
        for (int j=0;j<len;j+=i)
        {
            complex nw(1,0);
            for (int k=0;k<(i>>1);k++,nw=nw*wn)
            {
                complex x=a[j+k],y=nw*a[j+k+(i>>1)];
                a[j+k]=x+y,a[j+k+(i>>1)]=x-y;
            }
        }
    }
    if (flag==-1) for (int i=0;i<len;i++) a[i].x/=len;
}

int main()
{
    n=read();
    for (int i=0;i<n;i++) a[i].x=read(),b[n+1-i].x=read();
    for (m=1;m<=n*2;m<<=1)l++;
    for (int i=0;i<m;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
    fft(a,m,1),fft(b,m,1);
    for (int i=0;i<m;i++) ans[i]=a[i]*b[i];
    fft(ans,m,-1);
    for (int i=n+1;i<=n*2;i++) printf("%d\n",(int)(ans[i].x+0.5));
    return 0;
}

bzoj4827 [Hnoi2017]礼物:

假设移动a位,整体增加c,则答案为\(\sum_{i=1}^{n}{(x_{((i+a-1) \% n+1)}-y_{i}+c)^{2}}\)。把此式展开\(\sum _{i=1} ^{n} {x_{( (i+a-1) \% n+1 )} ^{2} +y_{i} ^{2} +c ^{2} +2 \times c \times x_{( (i+a-1) \% n+1 )} -2 \times y_{i} \times c -2 \times x_{( (i+a-1) \% n+1)} \times y_{i}}\)

\(\sum _{i=1} ^{n} {x_{( (i+a-1) \% n+1 )} \times y_{i}}\)可以用FFT求,之后枚举a和c就行了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 0x7f7f7f7f
using namespace std;
const int maxn=5e4;
const double pi=acos(-1);
int n,m,k,l,s,ans=1e9;
int a[maxn+10],b[maxn+10],rev[maxn*4+10];

int read()
{
    char ch;int x=0,f=1;
    for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}

struct complex
{
    double x,y;
    complex(){};
    complex (double _x,double _y){x=_x,y=_y;};
    complex operator +(const complex &a){return complex(x+a.x,y+a.y);}
    complex operator -(const complex &a){return complex(x-a.x,y-a.y);}
    complex operator *(const complex &a){return complex(x*a.x-y*a.y,x*a.y+y*a.x);}
}x1[maxn*4+10],x2[maxn*4+10];

void fft(complex *a,int len,int flag)
{
    for (int i=0;i<len;i++) if (rev[i]<i) swap(a[i],a[rev[i]]);
    for (int i=2;i<=len;i<<=1)
    {
        complex wn(cos(2*pi/i),sin(2*pi/i*flag));
        for (int j=0;j<len;j+=i)
        {
            complex nw(1,0);
            for (int k=0;k<(i>>1);k++,nw=nw*wn)
            {
                complex x=a[j+k],y=nw*a[j+k+(i>>1)];
                a[j+k]=x+y,a[j+k+(i>>1)]=x-y;
            }
        }
    }
    if (flag==-1) for (int i=0;i<len;i++) a[i].x/=len;
}

int main()
{
    n=read(),k=read();
    for (int i=0;i<n;i++) a[i]=read(),x1[i].x=a[i];
    for (int i=0;i<n;i++) b[i]=read(),x2[n-i-1].x=b[i],s+=b[i];
    for (m=1;m<=n*2;m<<=1)l++;
    for (int i=0;i<m;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
    fft(x1,m,1),fft(x2,m,1);
    for (int i=0;i<m;i++) x1[i]=x1[i]*x2[i];
    fft(x1,m,-1);
    for (int c=0;c<=k;c++)
    {
        int sum=0;
        for (int i=0;i<n;i++) sum+=(a[i]+c)*(a[i]+c)+b[i]*b[i];
        ans=min(ans,sum-2*((int)(x1[n-1].x+0.5)+s*c));
        for (int i=1;i<n;i++)
        {
            int tmp=(int)(x1[n+i-1].x+0.5)+(int)(x1[i-1].x+0.5)+s*c;
            ans=min(ans,sum-2*tmp);
        }
    }
    printf("%d\n",ans);
    return 0;
}

bzoj4555 [Tjoi2016&Heoi2016]求和

\(f_n=\sum_{i=0}^{n}\sum_{j=0}^{i}S_{i,j} \times 2^j \times j!\),对答案\(\%98244353\)。其中\(S_{i,j}\)为第二类斯特林数,意义为将n个不同的元素拆分成m个集合的方案数,所以当m>n时\(S_{n,m}\)为0。而\(S_{n,m} \times m!\)就是将n个不同的元素拆分成m个不同的集合的方案数。用容斥可得到\(S_{n,m} \times m!=\sum_{i=0}^{m}{(-1)^i \times \binom{m}{i} \times (m-i)^n}\)

好,那我们开始推式子。
\(f_n=\sum_{i=0}^{n}\sum_{j=0}^{i}S_{i,j} \times 2^j \times j!\)
\(f_n=\sum_{i=0}^{n}\sum_{j=0}^{n}{2^j \times\sum_{k=0}^{j}}(-1)^k \times \binom{j}{k} \times (j-k)^i\)
\(f_n=\sum_{j=0}^{n}2^j \times \sum_{k=0}^{j}(-1)^k \times \frac{j!}{k!(j-k)!} \times \sum_{i=0}^{n} (j-k)^i​\)
\(f_n=\sum_{j=0}^{n} 2^j \times j! \times \sum_{k=0}^{j} \frac{(-1)^k}{k!} \times \frac{\sum_{i=0}^{n}(j-k)^i}{(j-k)!}\)

后面的\(\sum_{k=0}^{j} \frac{(-1)^k}{k!} \times \frac{\sum_{i=0}^{n}(j-k)^i}{(j-k)!}\)就是一个卷积的形式,因为要膜,所以直接上NTT即可。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 0x7f7f7f7f
using namespace std;
const int mod=998244353;
const int maxn=1e5+5;
int ans,n,m,l;
int rev[maxn<<2],inv[maxn<<2],x[maxn<<2],y[maxn<<2];

int read()
{
    char ch;int x=0,f=1;
    for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}   

int power(int a,int k)
{
    int sum=1;
    for (;k;k>>=1,a=1ll*a*a%mod)
    if (k&1)
        sum=1ll*sum*a%mod;
    return sum;
}

void ntt(int *a,int len,int flag)
{
    for (int i=0;i<len;i++) if (rev[i]<i) swap(a[rev[i]],a[i]);
    for (int i=2;i<=len;i<<=1)
    {
        int gn=power(3,((mod-1)+flag*(mod-1)/i)%(mod-1));
        for (int j=0;j<len;j+=i)
        {
            int ng=1;
            for (int k=0;k<(i>>1);k++,ng=1ll*ng*gn%mod)
            {
                int x=a[j+k],y=1ll*ng*a[j+k+(i>>1)]%mod;
                a[j+k]=(x+y)%mod,a[j+k+(i>>1)]=1ll*((x-y)%mod+mod)%mod;
            }
        }
    }
    if (flag==-1)
    {
        int invlen=power(len,mod-2);
        for (int i=0;i<len;i++) x[i]=1ll*x[i]*invlen%mod;
    }
}

int main()
{
    n=read();
    for (m=1;m<n*2;m<<=1)l++;
    for (int i=0;i<m;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
    inv[0]=1;
    for (int i=1;i<=n;i++) inv[i]=1ll*inv[i-1]*i%mod;
    inv[n]=power(inv[n],mod-2);
    for (int i=n-1;i;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
    for (int i=0;i<=n;i++)  x[i]=(inv[i]*(i&1?-1:1)+mod)%mod;
    y[0]=1,y[1]=n+1;
    for (int i=2;i<=n;i++) y[i]=1ll*(power(i,n+1)-1)*power(i-1,mod-2)%mod*inv[i]%mod;
    ntt(x,m,1),ntt(y,m,1);
    for (int i=0;i<m;i++) x[i]=1ll*x[i]*y[i]%mod;
    ntt(x,m,-1);
    int two_power=1,fact=1;ans=x[0];
    for (int i=1;i<=n;i++)
    {
        two_power=1ll*two_power*2%mod,fact=1ll*fact*i%mod;
        ans=(ans+1ll*two_power*fact%mod*x[i]%mod)%mod;
    }
    printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/Alseo_Roplyer/p/9449755.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值