概述
FFT可快速计算卷积,卷积为两个生成函数的乘积,生成函数为
FFT 卷积 生成函数 的关系
多项式 之 快速傅里叶变换(FFT)/数论变换(NTT)/例题与常用套路【入门】
HDU1402 A * B Problem Plus
大数乘法
生成函数:,设两个大数位:、,L1/L2为两个大数的长度,
利用FFT求两个函数的卷积,将所得改为十进制整数即为所求答案
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+5;
const double PI=acos(-1.0);
typedef complex<double> C;
C a[MAX],b[MAX];
char s1[MAX],s2[MAX];
int ans[MAX];
int L;
void rader(C *c_)
{
int i,j,k;
for(i=1,j=L/2;i<L-1;++i)
{
if(i<j) swap(c_[i],c_[j]);
k=L/2;
while(j>=k){j-=k;k/=2;}
if(j<k) j+=k;
}
}
void fft(C *c_,int v)
{
rader(c_);
for(int i=2;i<=L;i<<=1)
{
C wn(cos(-2.0*v*PI/i),sin(-2.0*v*PI/i));
for(int j=0;j<L;j+=i)
{
C w(1,0);
for(int k=j;k<j+i/2;++k)
{
C u=c_[k];
C t=w*c_[k+i/2];
c_[k]=u+t;
c_[k+i/2]=u-t;
w=w*wn;
}
}
}
if(v==-1) for(int i=0;i<L;++i) c_[i].real(c_[i].real()/L);
}
int main()
{
while(~scanf("%s%s",s1,s2))
{
int L1=strlen(s1),L2=strlen(s2);
L=1;
while(L<L1<<1||L<L2<<1) L<<=1;
for(int i=0;i<L1;++i) a[i]=C(s1[L1-i-1]-'0',0);
for(int i=L1;i<L;++i) a[i]=C(0,0);
for(int i=0;i<L2;++i) b[i]=C(s2[L2-i-1]-'0',0);
for(int i=L2;i<L;++i) b[i]=C(0,0);
fft(a,1);fft(b,1);
for(int i=0;i<L;++i) a[i]=a[i]*b[i];
fft(a,-1);
for(int i=0;i<L;++i) ans[i]=(int)(a[i].real()+0.5);
for(int i=0;i<L;++i)
{
ans[i+1]+=ans[i]/10;
ans[i]%=10;
}
L=L1+L2-1;
while(ans[L]<=0&&L>0) --L;
for(int i=L;i>=0;--i)
printf("%d",ans[i]);
printf("n");
}
return 0;
}
SPOJ - TSUMSP Triple Sums
N个数中取3个,求和的值和其方案数
将N个数复制为3堆,从每堆中取出1个数,FFT+组合计数+容斥:
设一堆固定的生成函数为,两堆固定的生成函数为,三堆固定的生成函数为,答案的生成函数为
#include<bits/stdc++.h>
using namespace std;
const int MAX=1<<17;
const double PI=acos(-1.0);
typedef complex<double> C;
map<int,int>mp;
map<int,int>::iterator p;
C a1[MAX],a2[MAX],a3[MAX],ans[MAX];
int n,L,cnt[MAX];
void rader(C *c_)
{
int i,j,k;
for(i=1,j=L/2;i<L-1;++i)
{
if(i<j) swap(c_[i],c_[j]);
k=L/2;
while(j>=k){j-=k;k/=2;}
if(j<k) j+=k;
}
}
void fft(C *c_,int v)
{
rader(c_);
for(int i=2;i<=L;i<<=1)
{
C wn(cos(-2.0*v*PI/i),sin(-2.0*v*PI/i));
for(int j=0;j<L;j+=i)
{
C w(1,0);
for(int k=j;k<j+i/2;++k)
{
C u=c_[k];
C t=w*c_[k+i/2];
c_[k]=u+t;
c_[k+i/2]=u-t;
w=w*wn;
}
}
}
if(v==-1) for(int i=0;i<L;++i) c_[i].real(c_[i].real()/L);
}
int main()
{
int x;
scanf("%d",&n);
mp.clear();
for(int i=0;i<n;++i)
{
scanf("%d",&x);x+=20000;
if(mp[x]) ++mp[x];
else mp[x]=1;
}
for(p=mp.begin();p!=mp.end();++p) a1[p->first]=a2[p->first*2]=a3[p->first*3]=C(p->second,0);
L=MAX;
fft(a1,1);fft(a2,1);fft(a3,1);
for(int i=0;i<L;++i) ans[i]=a1[i]*a1[i]*a1[i]-a1[i]*a2[i]*3.0+a3[i]*2.0;
fft(ans,-1);
for(int i=0;i<L;++i)
{
long long tmp=(long long)(ans[i].real()+0.5)/6;
if(tmp) printf("%d : %I64dn",i-60000,tmp);
}
return 0;
}
HDU5829 Rikka with Subset
一个集合的价值是前K大的数之和,有N个数,求其各集合的价值之和
将N个数从大到小排列,所有树作为第大的贡献之和为:
即从前个数中选取个,使成为第大,因为其作为第大大仍有贡献,所以取这个数的所有子集个
将该式化为卷积形式:
,
,
求其前缀和
#include<bits/stdc++.h>
using namespace std;
const int MAX=2e5+5;
const long long MOD=998244353;
const int g=3;
long long fact[MAX],inv[MAX],bit[MAX],a[MAX<<1],b[MAX<<1],c[MAX],ni;
int t,n,L;
long long pow(long long a,int k,long long mod)//快速幂取模
{
long long b=1;
while(k)
{
if(k&1) b=b*a%mod;
a=a*a%mod;
k>>=1;
}
return b;
}
void init()
{
fact[0]=inv[0]=bit[0]=1;
for(int i=1;i<MAX;i++)
{
fact[i]=fact[i-1]*i%MOD;
inv[i]=inv[i-1]*pow(i,MOD-2,MOD)%MOD;//利用欧拉函数的积性
bit[i]=bit[i-1]*2%MOD;
}
}
void rader(long long *f_)
{
int i,j,k;
for(i=1,j=L/2;i<L-1;++i)
{
if(i<j) swap(f_[i],f_[j]);
k=L/2;
while(j>=k){j-=k;k>>=1;}
if(j<k) j+=k;
}
}
void ntt(long long *f_,int t)
{
rader(f_);
for(int i=2;i<=L;i<<=1)
{
long long wn=pow(g,(MOD-1)/i,MOD);
if(t==-1) wn=pow(wn,MOD-2,MOD);
for(int j=0;j<L;j+=i)
{
long long e=1;
for(int k=j;k<j+i/2;++k)
{
long long u=f_[k];
long long v=e*f_[k+i/2]%MOD;
f_[k]=(u+v)%MOD;
f_[k+i/2]=(u-v+MOD)%MOD;
e=e*wn%MOD;
}
}
}
if(t==-1) for(int i=0;i<L;++i) f_[i]=f_[i]*ni%MOD;
}
int main()
{
init();
scanf("%d",&t);
while(t--)
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
scanf("%d",&n);
L=1;
while(L<n<<1) L<<=1;
ni=pow(L,MOD-2,MOD);
for(int i=1;i<=n;++i) scanf("%I64d",&c[i]);
sort(c+1,c+n+1,greater<long long>());
for(int i=0;i<n;++i)
{
a[i]=bit[n-i]*inv[i]%MOD;
b[i]=c[n-i]*fact[n-i-1]%MOD;
}
ntt(a,1);ntt(b,1);
for(int i=0;i<L;++i) a[i]=a[i]*b[i]%MOD;
ntt(a,-1);
long long r=inv[2],ans=0;
for(int i=1;i<=n;++i)
{
ans=(ans+a[n-i]*inv[i-1]%MOD*r%MOD)%MOD;
r=r*inv[2]%MOD;
printf("%I64d ",ans);
}
printf("n");
}
return 0;
}
FWT
void FWT_or(int *a,int opt)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
if(opt==1)a[i+j+k]=(a[j+k]+a[i+j+k])%MOD;
else a[i+j+k]=(a[i+j+k]+MOD-a[j+k])%MOD;
}
void FWT_and(int *a,int opt)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
if(opt==1)a[j+k]=(a[j+k]+a[i+j+k])%MOD;
else a[j+k]=(a[j+k]+MOD-a[i+j+k])%MOD;
}
void FWT_xor(int *a,int opt)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
{
int X=a[j+k],Y=a[i+j+k];
a[j+k]=(X+Y)%MOD;a[i+j+k]=(X+MOD-Y)%MOD;
if(opt==-1)a[j+k]=1ll*a[j+k]*inv2%MOD,a[i+j+k]=1ll*a[i+j+k]*inv2%MOD;
}
}
最后
以上就是矮小白昼为你收集整理的FFT/NTT/FWT专题的全部内容,希望文章能够帮你解决FFT/NTT/FWT专题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复