感觉蒟蒻现在学多项式求逆貌似有些晚了
不过真的很有意思了(然而省选的时候自己还在玩泥巴什么也不会
多项式求逆是基于倍增的
假设我们知道
h(x)f(x)=1(mod x^n)
移项得
(h(x)*f(x)-1)=0(mod x^n)
两边同时求平方得
h(x)^2*f(x)^2 - 2*h(x)*f(x) +1 = 0 (mod x^2n)
设g(x)*f(x)=1(mod x^2n)
两边同时乘以g(x)可以得
h(x)^2*f(x) -2*h(x) + g(x) =0 (mod x^2n)
我们移项可以得到
g(x) = h(x) *(2- f(x)*h(x))
倍增即可,时间复杂度O(nlogn)
首先是picks的blog里提到的伯努利数
由于并没有找到相应的题目,于是就在cojs上自己出了一发
疯狂的求和问题 80分
我们知道伯努利数的生成函数是x/(e^x-1)
我们把下面做泰勒展开之后进行多项式求逆即可
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#define G 3
using namespace std;
typedef long long LL;
const int mod=998244353;
const int maxn=500010;
int n,k,N,len,ans;
int jc[maxn],inv[maxn];
int rev[maxn];
int A[maxn],B[maxn],C[maxn];
void read(int &num){
num=0;char ch=getchar();
while(ch<'!')ch=getchar();
while(ch>='0'&&ch<='9'){
num=(10LL*num+ch-'0')%mod;
ch=getchar();
}return;
}
int pow_mod(int v,int p){
int tmp=1;
while(p){
if(p&1)tmp=1LL*tmp*v%mod;
v=1LL*v*v%mod;p>>=1;
}return tmp;
}
void FFT(int *A,int n,int flag){
for(len=0;(1<<len)<n;++len);
for(int i=0;i<n;++i){
rev[i]=rev[i>>1]>>1|((i&1)<<(len-1));
C[i]=A[rev[i]];
}
for(int i=0;i<n;++i)A[i]=C[i];
for(int k=2;k<=n;k<<=1){
int mk=(k>>1);
int wn=pow_mod(G,flag==1?(mod-1)/k:(mod-1)-(mod-1)/k);
for(int i=0;i<n;i+=k){
int w=1;
for(int j=0;j<mk;++j){
int a=i+j,b=i+j+mk;
int x=A[a],y=1LL*A[b]*w%mod;
A[a]=x+y;if(A[a]>=mod)A[a]-=mod;
A[b]=x-y;if(A[b]<0)A[b]+=mod;
w=1LL*w*wn%mod;
}
}
}
if(flag==-1){
int c=pow_mod(n,mod-2);
for(int i=0;i<n;++i)A[i]=1LL*A[i]*c%mod;
}return;
}
void Get_inv(int n){
if(n==1){
B[0]=pow_mod(A[0],mod-2);
return;
}
int now=(n>>1);Get_inv(now);
static int tmp[maxn];
for(int i=0;i<n;++i)tmp[i]=A[i];
now=(n<<1);
FFT(B,now,1);FFT(tmp,now,1);
for(int i=0;i<now;++i)B[i]=1LL*B[i]*(2LL-1LL*tmp[i]*B[i]%mod+mod)%mod;
FFT(B,now,-1);
memset(B+n,0,sizeof(int)*n);
}
int Get_C(int n,int m){return 1LL*jc[n]*inv[m]%mod*inv[n-m]%mod;}
int main(){
freopen("Crazy_Sum.in","r",stdin);
freopen("Crazy_Sum.out","w",stdout);
read(n);scanf("%d",&k);
for(N=1;N<=k;N<<=1);
jc[0]=1;
for(int i=1;i<=N;++i)jc[i]=1LL*jc[i-1]*i%mod;
inv[N]=pow_mod(jc[N],mod-2);
for(int i=N-1;i>=0;--i)inv[i]=1LL*inv[i+1]*(i+1)%mod;
for(int i=0;i<N;++i)A[i]=inv[i+1];
Get_inv(N);
for(int i=0;i<N;++i)B[i]=1LL*B[i]*jc[i]%mod;
int now=n+1;
for(int i=1;i<=k+1;++i){
ans=ans+1LL*Get_C(k+1,i)*B[k+1-i]%mod*now%mod;
if(ans>=mod)ans-=mod;
now=1LL*now*(n+1)%mod;
}ans=1LL*ans*pow_mod(k+1,mod-2)%mod;
printf("%d\n",ans);
return 0;
}
BZOJ 3456
很经典的题目啦,设fi表示i个点的联通图的个数,考虑1号点所在联通块的大小我们有
fn=2^C(n,2)-sigma(C(n-1,i-1)*fi*2^C(n-i,2))
很容易发现这是个CDQ+FFT的式子,直接上就可以了
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#define G 3
using namespace std;
const int mod=1004535809;
const int maxn=300010;
int n,N,len;
int jc[maxn],inv[maxn],rev[maxn];
int h[maxn],f[maxn];
int A[maxn],B[maxn],C[maxn];
int pow_mod(int v,int p){
int tmp=1;
while(p){
if(p&1)tmp=1LL*tmp*v%mod;
v=1LL*v*v%mod;p>>=1;
}return tmp;
}
void FFT(int *A,int n,int flag){
for(int i=0;i<n;++i)if(i<rev[i])swap(A[i],A[rev[i]]);
for(int k=2;k<=n;k<<=1){
int mk=(k>>1);
int wn=pow_mod(G,flag==1?(mod-1)/k:(mod-1)-(mod-1)/k);
for(int i=0;i<n;i+=k){
int w=1;
for(int j=0;j<mk;++j){
int a=i+j,b=i+j+mk;
int x=A[a],y=1LL*A[b]*w%mod;
A[a]=x+y;if(A[a]>=mod)A[a]-=mod;
A[b]=x-y;if(A[b]<0)A[b]+=mod;
w=1LL*w*wn%mod;
}
}
}
if(flag==-1){
int c=pow_mod(n,mod-2);
for(int i=0;i<n;++i)A[i]=1LL*A[i]*c%mod;
}return;
}
void Solve(int L,int R){
if(L==R)return;
int mid=(L+R)>>1;
Solve(L,mid);
for(N=1,len=0;N<=(R-L+1);N<<=1,len++);
for(int i=0;i<N;++i)A[i]=0,B[i]=h[i];
for(int i=L;i<=mid;++i)A[i-L]=1LL*f[i]*inv[i-1]%mod;
if(R-L<=500){
int lim=R-L+1;
for(int i=0;i<lim;++i){
C[i]=0;
for(int j=0;j<=i;++j){
C[i]=C[i]+1LL*A[j]*B[i-j]%mod;
if(C[i]>=mod)C[i]-=mod;
}
}
}else{
for(int i=0;i<N;++i)rev[i]=rev[i>>1]>>1|((i&1)<<(len-1));
FFT(A,N,1);FFT(B,N,1);
for(int i=0;i<N;++i)C[i]=1LL*A[i]*B[i]%mod;
FFT(C,N,-1);
}
for(int i=mid+1;i<=R;++i){
f[i]=f[i]-1LL*jc[i-1]*C[i-L]%mod;
if(f[i]<0)f[i]+=mod;
}
Solve(mid+1,R);
}
int main(){
scanf("%d",&n);
jc[0]=1;
for(int i=1;i<=n;++i)jc[i]=1LL*jc[i-1]*i%mod;
inv[n]=pow_mod(jc[n],mod-2);
for(int i=n-1;i>=0;--i)inv[i]=1LL*inv[i+1]*(i+1)%mod;
h[1]=1;f[1]=1;
for(int i=2;i<=n;++i){
int tmp=(1LL*i*(i-1)/2)%(mod-1);
h[i]=pow_mod(2,tmp);f[i]=h[i];
h[i]=1LL*h[i]*inv[i]%mod;
}
Solve(1,n);
printf("%d\n",f[n]);
return 0;
}
但是这样我们是两个log的
我们注意到可以把上面的式子等价转化成
2^C(n,2)/(n-1)!=sigma( 2^C(n-i,2)/(n-i)! *fi/(i-1)! )
可以构造出h=g*f的形式
之后我们已知h和g,求f,求h*g^(-1)即可,也就是多项式求逆
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define G 3
using namespace std;
typedef long long LL;
const int maxn=500010;
const int mod=1004535809;
int n,N,len,t;
int jc[maxn],inv[maxn],rev[maxn];
int A[maxn],B[maxn];
int pow_mod(int v,int p){
int tmp=1;
while(p){
if(p&1)tmp=1LL*tmp*v%mod;
v=1LL*v*v%mod;p>>=1;
}return tmp;
}
void FFT(int *A,int n,int flag){
for(int i=0;i<n;++i){
if(i<rev[i]){t=A[i];A[i]=A[rev[i]];A[rev[i]]=t;}
}
for(int k=2;k<=n;k<<=1){
int mk=(k>>1);
int wn=pow_mod(G,flag==1?(mod-1)/k:(mod-1)-(mod-1)/k);
for(int i=0;i<n;i+=k){
int w=1;
for(int j=0;j<mk;++j){
int a=i+j,b=i+j+mk;
int x=A[a],y=1LL*A[b]*w%mod;
A[a]=x+y;if(A[a]>=mod)A[a]-=mod;
A[b]=x-y;if(A[b]<0)A[b]+=mod;
w=1LL*w*wn%mod;
}
}
}
if(flag==-1){
int c=pow_mod(n,mod-2);
for(int i=0;i<n;++i)A[i]=1LL*A[i]*c%mod;
}return;
}
void Get_inv(int n){
if(n==1){
B[0]=pow_mod(A[0],mod-2);
return;
}
Get_inv(n>>1);
int now=(n<<1);
for(len=0;(1<<len)<now;++len);
for(int i=0;i<now;++i)rev[i]=rev[i>>1]>>1|((i&1)<<(len-1));
static int tmp[maxn];
for(int i=0;i<n;++i)tmp[i]=A[i];
FFT(tmp,now,1);FFT(B,now,1);
for(int i=0;i<now;++i)B[i]=1LL*B[i]*(2LL-1LL*tmp[i]*B[i]%mod+mod)%mod;
FFT(B,now,-1);
memset(B+n,0,sizeof(int)*n);
}
int main(){
scanf("%d",&n);
for(N=1;N<=n;N<<=1);
jc[0]=1;
for(int i=1;i<N;++i)jc[i]=1LL*jc[i-1]*i%mod;
inv[N-1]=pow_mod(jc[N-1],mod-2);
for(int i=N-2;i>=0;--i)inv[i]=1LL*inv[i+1]*(i+1)%mod;
for(int i=0;i<N;++i){
int tmp=(1LL*i*(i-1)/2)%(mod-1);
A[i]=1LL*pow_mod(2,tmp)*inv[i]%mod;
}
Get_inv(N);
memset(B+n,0,sizeof(int)*n);
for(len=0;(1<<len)<N;++len);
for(int i=0;i<N;++i)rev[i]=rev[i>>1]>>1|((i&1)<<(len-1));
for(int i=0;i<N;++i){
int tmp=(1LL*i*(i-1)/2)%(mod-1);
A[i]=1LL*pow_mod(2,tmp)*inv[i-1]%mod;
}
for(len=0;(1<<len)<N;++len);
for(int i=0;i<N;++i)rev[i]=rev[i>>1]>>1|((i&1)<<(len-1));
FFT(A,N,1);FFT(B,N,1);
for(int i=0;i<N;++i)A[i]=1LL*A[i]*B[i]%mod;
FFT(A,N,-1);
printf("%d\n",1LL*A[n]*jc[n-1]%mod);
return 0;
}
cojs 疯狂的欧拉图
搞完了城市规划之后自己把原来的考试题扩大了数据范围出到了cojs上(原来n^2可过)
式子还是一样的推导,之后也可以转换成多项式求逆
但是由于模数不兹瓷,所以我们要写模任意质数的FFT QAQ
CDQ+FFT
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define G 3
using namespace std;
const int maxn=400010;
typedef long long LL;
int n,N,len;
const int mod=1e9+7;
const int M=30000;
const long double pi=acos(-1.0);
int jc[maxn],inv[maxn];
int h[maxn],f[maxn];
int rev[maxn];
int a[maxn],b[maxn],c[maxn];
int a0[maxn],b0[maxn],a1[maxn],b1[maxn];
struct cpx{
long double r,i;
cpx(long double r=0,long double i=0):r(r),i(i){}
}A[maxn],B[maxn],C[maxn];
cpx operator +(const cpx &A,const cpx &B){return cpx(A.r+B.r,A.i+B.i);}
cpx operator -(const cpx &A,const cpx &B){return cpx(A.r-B.r,A.i-B.i);}
cpx operator *(const cpx &A,const cpx &B){return cpx(A.r*B.r-A.i*B.i,A.r*B.i+A.i*B.r);}
void FFT(cpx *A,int n,int type){
for(int i=0;i<n;++i)if(i<rev[i])swap(A[i],A[rev[i]]);
for(int k=0;(1<<k)<n;++k){
int m=(1<<k),m2=(m<<1);
long double o=2*pi/m2*type;
cpx wn(cos(o),sin(o));
for(int i=0;i<n;i+=m2){
cpx w(1,0);
for(int j=0;j<m;++j){
cpx x=A[i+j],y=A[i+j+m]*w;
A[i+j]=x+y;A[i+j+m]=x-y;
w=w*wn;
}
}
}
if(type==-1)for(int i=0;i<n;++i)A[i].r/=n;
}
void mul(int *a,int *b,int *c){
for(int i=0;i<N;++i)A[i]=cpx(a[i],0),B[i]=cpx(b[i],0);
FFT(A,N,1);FFT(B,N,1);
for(int i=0;i<N;++i)A[i]=A[i]*B[i];
FFT(A,N,-1);
for(int i=0;i<N;++i)c[i]=((LL)(A[i].r+0.5))%mod;
}
void mul_mod(int *a,int *b,int *c){
for(int i=0;i<N;++i)a0[i]=a[i]/M,b0[i]=b[i]/M;
mul(a0,b0,a0);
for(int i=0;i<N;++i){
c[i]=1LL*a0[i]*M*M%mod;
a1[i]=a[i]%M;b1[i]=b[i]%M;
}
mul(a1,b1,a1);
for(int i=0;i<N;++i){
c[i]=c[i]+a1[i];
if(c[i]>=mod)c[i]-=mod;
a1[i]=a1[i]+a0[i];
if(a1[i]>=mod)a1[i]-=mod;
a0[i]=a[i]/M+a[i]%M;
b0[i]=b[i]/M+b[i]%M;
}
mul(a0,b0,a0);
for(int i=0;i<N;++i){
c[i]=c[i]+1LL*M*(a0[i]-a1[i]+mod)%mod;
if(c[i]>=mod)c[i]-=mod;
}return;
}
LL pow_mod(LL v,int p){
LL tmp=1;
while(p){
if(p&1)tmp=tmp*v%mod;
v=v*v%mod;p>>=1;
}return tmp;
}
void Solve(int L,int R){
if(L==R)return;
int mid=(L+R)>>1;
Solve(L,mid);
for(N=1,len=0;N<(R-L+1);N<<=1,len++);
for(int i=0;i<N;++i)a[i]=b[i]=0;
for(int i=L;i<=mid;++i)a[i-L]=1LL*f[i]*inv[i-1]%mod;
for(int i=0;i<N;++i)b[i]=h[i];
if(R-L<=1000){
int lim=R-L+1;
for(int i=0;i<lim;++i){
c[i]=0;
for(int j=0;j<=i;++j){
c[i]=c[i]+1LL*a[j]*b[i-j]%mod;
if(c[i]>=mod)c[i]-=mod;
}
}
}else{
for(int i=0;i<N;++i)rev[i]=rev[i>>1]>>1|((i&1)<<(len-1));
mul_mod(a,b,c);
}
for(int i=mid+1;i<=R;++i){
f[i]=f[i]-1LL*jc[i-1]*c[i-L]%mod;
if(f[i]<0)f[i]+=mod;
}
Solve(mid+1,R);
}
int main(){
freopen("Crazy_Graph.in","r",stdin);
freopen("Crazy_Graph.out","w",stdout);
scanf("%d",&n);
jc[0]=1;
for(int i=1;i<=n;++i)jc[i]=1LL*jc[i-1]*i%mod;
inv[n]=pow_mod(jc[n],mod-2);
for(int i=n-1;i>=0;--i)inv[i]=1LL*inv[i+1]*(i+1)%mod;
h[1]=1;f[1]=1;
for(int i=2;i<=n;++i){
h[i]=pow_mod(2LL,(1LL*(i-1)*(i-2)/2)%(mod-1));
f[i]=h[i];
h[i]=1LL*h[i]*inv[i]%mod;
}
Solve(1,n);
f[n]=f[n]+1LL*n*(n-1)*pow_mod(2LL,mod-2)%mod*f[n]%mod;
if(f[n]>=mod)f[n]-=mod;
printf("%d\n",f[n]);
return 0;
}
多项式求逆
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std;
typedef long long LL;
const int mod=1e9+7;
const int M=32000;
const int maxn=400010;
const long double pi=acos(-1.0);
int n,len,N;
int jc[maxn],inv[maxn],rev[maxn];
int a[maxn],b[maxn],c[maxn];
int a0[maxn],b0[maxn],a1[maxn],b1[maxn];
struct cpx{
long double r,i;
cpx(long double r=0,long double i=0):r(r),i(i){}
}A[maxn],B[maxn];
cpx operator +(const cpx &A,const cpx &B){return cpx(A.r+B.r,A.i+B.i);}
cpx operator -(const cpx &A,const cpx &B){return cpx(A.r-B.r,A.i-B.i);}
cpx operator *(const cpx &A,const cpx &B){return cpx(A.r*B.r-A.i*B.i,A.r*B.i+A.i*B.r);}
int pow_mod(int v,int p){
int tmp=1;
while(p){
if(p&1)tmp=1LL*tmp*v%mod;
v=1LL*v*v%mod;p>>=1;
}return tmp;
}
void FFT(cpx *A,int n,int flag){
for(int i=0;i<n;++i)if(i<rev[i])swap(A[i],A[rev[i]]);
for(int k=2;k<=n;k<<=1){
int mk=(k>>1);
long double o=2*pi/k*flag;
cpx wn(cos(o),sin(o));
for(int i=0;i<n;i+=k){
cpx w(1,0);
for(int j=0;j<mk;++j){
int a=i+j,b=i+j+mk;
cpx x=A[a],y=A[b]*w;
A[a]=x+y;A[b]=x-y;
w=w*wn;
}
}
}
if(flag==-1){for(int i=0;i<n;++i)A[i].r/=n;}
}
void mul(int *a,int *b,int *c,int N){
for(int i=0;i<N;++i)A[i]=cpx(a[i],0),B[i]=cpx(b[i],0);
FFT(A,N,1);FFT(B,N,1);
for(int i=0;i<N;++i)A[i]=A[i]*B[i];
FFT(A,N,-1);
for(int i=0;i<N;++i)c[i]=(LL)(A[i].r+0.5)%mod;
}
void mul_mod(int *A,int *B,int *C,int N){
for(int i=0;i<N;++i)a0[i]=A[i]%M,b0[i]=B[i]%M;
mul(a0,b0,a0,N);
for(int i=0;i<N;++i)C[i]=a0[i],a1[i]=A[i]/M,b1[i]=B[i]/M;
mul(a1,b1,a1,N);
for(int i=0;i<N;++i){
C[i]=C[i]+1LL*M*M*a1[i]%mod;
if(C[i]>=mod)C[i]-=mod;
a1[i]=a1[i]+a0[i];
if(a1[i]>=mod)a1[i]-=mod;
a0[i]=A[i]/M+A[i]%M;
b0[i]=B[i]/M+B[i]%M;
}
mul(a0,b0,a0,N);
for(int i=0;i<N;++i){
C[i]=C[i]+1LL*M*(a0[i]-a1[i]+mod)%mod;
if(C[i]>=mod)C[i]-=mod;
}return;
}
void Get_inv(int n){
if(n==1){
b[0]=pow_mod(a[0],mod-2);
return;
}
Get_inv(n>>1);
int now=(n<<1);
for(len=0;(1<<len)<now;++len);
for(int i=0;i<now;++i)rev[i]=rev[i>>1]>>1|((i&1)<<(len-1));
static int tmp[maxn];
for(int i=0;i<n;++i)tmp[i]=a[i];
mul_mod(b,tmp,c,now);
c[0]=2-c[0];
if(c[0]<0)c[0]+=mod;
for(int i=1;i<now;++i)c[i]=mod-c[i];
mul_mod(b,c,tmp,now);
for(int i=0;i<n;++i)b[i]=tmp[i];
memset(b+n,0,sizeof(int)*n);
}
int main(){
freopen("Crazy_Graph.in","r",stdin);
freopen("Crazy_Graph.out","w",stdout);
scanf("%d",&n);
for(N=1;N<=n;N<<=1);
jc[0]=1;
for(int i=1;i<N;++i)jc[i]=1LL*jc[i-1]*i%mod;
inv[N-1]=pow_mod(jc[N-1],mod-2);
for(int i=N-2;i>=0;--i)inv[i]=1LL*inv[i+1]*(i+1)%mod;
a[0]=1;
for(int i=1;i<N;++i){
int tmp=(1LL*(i-1)*(i-2)/2)%(mod-1);
a[i]=1LL*pow_mod(2,tmp)*inv[i]%mod;
}
Get_inv(N);
memset(b+n,0,sizeof(int)*n);
for(len=0;(1<<len)<N;++len);
for(int i=0;i<N;++i)rev[i]=rev[i>>1]>>1|((i&1)<<(len-1));
a[0]=1;
for(int i=1;i<N;++i){
int tmp=(1LL*(i-1)*(i-2)/2)%(mod-1);
a[i]=1LL*pow_mod(2,tmp)*inv[i-1]%mod;
}
mul_mod(a,b,c,N);
int ans=1LL*jc[n-1]*c[n]%mod;
ans=ans+1LL*n*(n-1)*pow_mod(2LL,mod-2)%mod*ans%mod;
if(ans>=mod)ans-=mod;
printf("%d\n",ans);
return 0;
}
cojs 异化多肽
貌似是多项式求逆的裸题,Nescafe的题目
构造生成函数f,不难发现我们要求的是f^1+f^2+……
之后求和得 1/(1-f)
多项式求逆即可
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#define G 5
using namespace std;
typedef long long LL;
const int maxn=300010;
const int mod=1005060097;
int n,m,k,N,L;
int A[maxn],B[maxn];
int rev[maxn];
void read(int &num){
num=0;char ch=getchar();
while(ch<'!')ch=getchar();
while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
int pow_mod(int v,int p){
int tmp=1;
while(p){
if(p&1)tmp=1LL*tmp*v%mod;
v=1LL*v*v%mod;p>>=1;
}return tmp;
}
void FFT(int *A,int n,int flag){
for(L=0;(1<<L)<n;++L);
for(int i=0;i<n;++i)rev[i]=rev[i>>1]>>1|((i&1)<<(L-1));
for(int i=0;i<n;++i)if(i<rev[i])swap(A[i],A[rev[i]]);
for(int k=2;k<=n;k<<=1){
int mk=(k>>1);
int wn=pow_mod(G,flag==1?(mod-1)/k:(mod-1)-(mod-1)/k);
for(int i=0;i<n;i+=k){
int w=1;
for(int j=0;j<mk;++j){
int x=A[i+j],y=1LL*A[i+j+mk]*w%mod;
A[i+j]=x+y;if(A[i+j]>=mod)A[i+j]-=mod;
A[i+j+mk]=x-y;if(A[i+j+mk]<0)A[i+j+mk]+=mod;
w=1LL*w*wn%mod;
}
}
}
if(flag==-1){
int inv=pow_mod(n,mod-2);
for(int i=0;i<n;++i)A[i]=1LL*A[i]*inv%mod;
}return;
}
void Get_inv(int n){
if(n==1){
B[0]=pow_mod(A[0],mod-2);
return;
}
int now=(n>>1);
Get_inv(now);
static int tmp[maxn];
for(int i=0;i<now;++i)tmp[i]=A[i];
FFT(tmp,n,1);FFT(B,n,1);
for(int i=0;i<n;++i)B[i]=1LL*B[i]*(2-1LL*tmp[i]*B[i]%mod+mod)%mod;
FFT(B,n,-1);
memset(B+now,0,sizeof(int)*now);
}
int main(){
freopen("polypeptide.in","r",stdin);
freopen("polypeptide.out","w",stdout);
read(n);read(m);
for(int i=1;i<=m;++i){
read(k);
if(k<=n)A[k]++;
}A[0]--;if(A[0]<0)A[0]+=mod;
for(N=1;N<=n;N<<=1);N<<=1;
Get_inv(N);
int ans=-B[n];
ans=(ans%mod+mod)%mod;
printf("%d\n",ans);
return 0;
}
HEOI 2016 求和
QAQ 当前考场上没推出式子来
其实当时并不是很会FFT,现在也不是很会
我们不难发现如果没有2^j的话求的是n个数划分成若干个有序集合的方案数
设fn为这个方案数,考虑枚举第一个集合的大小
我们有fn=sigma( C(n,i) * f(n-i) )
考虑2^j实际上是每个集合都要*2
那么我们的递推式只要改成 fn=sigma( C(n,i) * f(n-i) *2)就可以了
然后上CDQ+FFT
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define G 3
using namespace std;
typedef long long LL;
const int maxn=500010;
const int mod=998244353;
int n,N,len,ans;
int jc[maxn],inv[maxn],rev[maxn];
int f[maxn],A[maxn],B[maxn],C[maxn];
int pow_mod(int v,int p){
int tmp=1;
while(p){
if(p&1)tmp=1LL*tmp*v%mod;
v=1LL*v*v%mod;p>>=1;
}return tmp;
}
void FFT(int *A,int n,int flag){
for(int i=0;i<n;++i)if(i<rev[i])swap(A[i],A[rev[i]]);
for(int k=2;k<=n;k<<=1){
int mk=(k>>1);
int wn=pow_mod(G,flag==1?(mod-1)/k:(mod-1)-(mod-1)/k);
for(int i=0;i<n;i+=k){
int w=1;
for(int j=0;j<mk;++j){
int a=i+j,b=i+j+mk;
int x=A[a],y=1LL*A[b]*w%mod;
A[a]=x+y;if(A[a]>=mod)A[a]-=mod;
A[b]=x-y;if(A[b]<0)A[b]+=mod;
w=1LL*w*wn%mod;
}
}
}
if(flag==-1){
int c=pow_mod(n,mod-2);
for(int i=0;i<n;++i)A[i]=1LL*A[i]*c%mod;
}return;
}
void Solve(int L,int R){
if(L==R)return;
int mid=(L+R)>>1;
Solve(L,mid);
for(N=1,len=0;N<=(R-L+1);N<<=1,len++);
for(int i=0;i<N;++i)A[i]=0,B[i]=inv[i];
for(int i=L;i<=mid;++i)A[i-L]=f[i];
if(R-L<=500){
int lim=R-L+1;
for(int i=0;i<lim;++i){
C[i]=0;
for(int j=0;j<=i;++j){
C[i]=C[i]+1LL*A[j]*B[i-j]%mod;
if(C[i]>=mod)C[i]-=mod;
}
}
}else{
for(int i=0;i<N;++i)rev[i]=rev[i>>1]>>1|((i&1)<<(len-1));
FFT(A,N,1);FFT(B,N,1);
for(int i=0;i<N;++i)C[i]=1LL*A[i]*B[i]%mod;
FFT(C,N,-1);
}
for(int i=mid+1;i<=R;++i){
f[i]=f[i]+2LL*C[i-L]%mod;
if(f[i]>=mod)f[i]-=mod;
}
Solve(mid+1,R);
}
int main(){
freopen("heoi2016_sum.in","r",stdin);
freopen("heoi2016_sum.out","w",stdout);
scanf("%d",&n);
jc[0]=1;
for(int i=1;i<=n;++i)jc[i]=1LL*jc[i-1]*i%mod;
inv[n]=pow_mod(jc[n],mod-2);f[0]=1;
for(int i=n-1;i>=0;--i)inv[i]=1LL*inv[i+1]*(i+1)%mod;
Solve(0,n);
for(int i=0;i<=n;++i){
ans=ans+1LL*jc[i]*f[i]%mod;
if(ans>=mod)ans-=mod;
}printf("%d\n",ans);
return 0;
}
之后我们考虑化简一下式子
我们有fn-sigma(C(n,i)*fi*2)=0
之后构造多项式可以得到f - g*f =1 (因为f0=1)
则f = 1/(1-g)
多项式求逆即可
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#define G 3
using namespace std;
typedef long long LL;
const int mod=998244353;
const int maxn=300010;
int n,N,len,ans=0;
int jc[maxn],inv[maxn],rev[maxn];
int A[maxn],B[maxn];
int pow_mod(int v,int p){
int tmp=1;
while(p){
if(p&1)tmp=1LL*tmp*v%mod;
v=1LL*v*v%mod;p>>=1;
}return tmp;
}
void FFT(int *A,int n,int flag){
for(int i=0;i<n;++i)if(i<rev[i])swap(A[i],A[rev[i]]);
for(int k=2;k<=n;k<<=1){
int mk=(k>>1);
int wn=pow_mod(G,flag==1?(mod-1)/k:(mod-1)-(mod-1)/k);
for(int i=0;i<n;i+=k){
int w=1;
for(int j=0;j<mk;++j){
int a=i+j,b=i+j+mk;
int x=A[a],y=1LL*A[b]*w%mod;
A[a]=x+y;if(A[a]>=mod)A[a]-=mod;
A[b]=x-y;if(A[b]<0)A[b]+=mod;
w=1LL*w*wn%mod;
}
}
}
if(flag==-1){
int c=pow_mod(n,mod-2);
for(int i=0;i<n;++i)A[i]=1LL*A[i]*c%mod;
}return;
}
void Get_inv(int n){
if(n==1){
B[0]=pow_mod(A[0],mod-2);
return;
}
Get_inv(n>>1);
int now=(n<<1);
for(len=0;(1<<len)<now;++len);
for(int i=0;i<now;++i)rev[i]=rev[i>>1]>>1|((i&1)<<(len-1));
static int tmp[maxn];
for(int i=0;i<n;++i)tmp[i]=A[i];
FFT(tmp,now,1);FFT(B,now,1);
for(int i=0;i<now;++i)B[i]=1LL*B[i]*(2LL-1LL*tmp[i]*B[i]%mod+mod)%mod;
FFT(B,now,-1);
memset(B+n,0,sizeof(int)*n);
}
int main(){
freopen("heoi2016_sum.in","r",stdin);
freopen("heoi2016_sum.out","w",stdout);
scanf("%d",&n);
for(N=1;N<=n;N<<=1);
jc[0]=1;
for(int i=1;i<N;++i)jc[i]=1LL*jc[i-1]*i%mod;
inv[N-1]=pow_mod(jc[N-1],mod-2);
for(int i=N-2;i>=0;--i)inv[i]=1LL*inv[i+1]*(i+1)%mod;
for(int i=1;i<N;++i){
A[i]=(inv[i]<<1);
if(A[i]>=mod)A[i]-=mod;
}A[0]--;if(A[0]<0)A[0]+=mod;
Get_inv(N);
for(int i=0;i<=n;++i){
ans=ans+1LL*(mod-B[i])*jc[i]%mod;
if(ans>=mod)ans-=mod;
}printf("%d\n",ans);
return 0;
}
另外,求贝尔数的CDQ+FFT的程序
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#define G 11
using namespace std;
typedef long long LL;
const int maxn=300010;
const int mod=786433;
int n,N,len;
int jc[maxn],inv[maxn],rev[maxn];
int f[maxn],A[maxn],B[maxn],C[maxn];
int pow_mod(int v,int p){
int tmp=1;
while(p){
if(p&1)tmp=1LL*tmp*v%mod;
v=1LL*v*v%mod;p>>=1;
}return tmp;
}
void FFT(int *A,int n,int flag){
for(int i=0;i<n;++i)if(i<rev[i])swap(A[i],A[rev[i]]);
for(int k=2;k<=n;k<<=1){
int mk=(k>>1);
int wn=pow_mod(G,flag==1?(mod-1)/k:(mod-1)-(mod-1)/k);
for(int i=0;i<n;i+=k){
int w=1;
for(int j=0;j<mk;++j){
int a=i+j,b=i+j+mk;
int x=A[a],y=1LL*A[b]*w%mod;
A[a]=x+y;if(A[a]>=mod)A[a]-=mod;
A[b]=x-y;if(A[b]<0)A[b]+=mod;
w=1LL*w*wn%mod;
}
}
}
if(flag==-1){
int c=pow_mod(n,mod-2);
for(int i=0;i<n;++i)A[i]=1LL*A[i]*c%mod;
}return;
}
void Solve(int L,int R){
if(L==R)return;
int mid=(L+R)>>1;
Solve(L,mid);
for(N=1,len=0;N<=(R-L+1);N<<=1,len++);
A[0]=B[0]=0;
for(int i=1;i<N;++i)A[i]=0,B[i]=inv[i-1];
for(int i=L;i<=mid;++i)A[i-L]=1LL*f[i]*inv[i]%mod;
if(R-L<=500){
int lim=R-L+1;
for(int i=0;i<lim;++i){
C[i]=0;
for(int j=0;j<=i;++j){
C[i]=C[i]+1LL*A[j]*B[i-j]%mod;
if(C[i]>=mod)C[i]-=mod;
}
}
}else{
for(int i=0;i<N;++i)rev[i]=rev[i>>1]>>1|((i&1)<<(len-1));
FFT(A,N,1);FFT(B,N,1);
for(int i=0;i<N;++i)C[i]=1LL*A[i]*B[i]%mod;
FFT(C,N,-1);
}
for(int i=mid+1;i<=R;++i){
f[i]=f[i]+1LL*jc[i-1]*C[i-L]%mod;
if(f[i]>=mod)f[i]-=mod;
}Solve(mid+1,R);
}
int main(){
scanf("%d",&n);
jc[0]=1;
for(int i=1;i<=n;++i)jc[i]=1LL*jc[i-1]*i%mod;
inv[n]=pow_mod(jc[n],mod-2);
for(int i=n-1;i>=0;--i)inv[i]=1LL*inv[i+1]*(i+1)%mod;
f[0]=1;Solve(0,n);
printf("%d\n",f[n]);
return 0;
}
但是我并不知道怎么转成多项式求逆,如果有老司机知道还请告诉我QAQ
留下的一些坑:
1、城市规划的多项式求ln的写法
2、多项式开根
3、贝尔数的多项式求exp的写法
感觉很多CDQ+FFT都能很轻松的转成多项式求逆啊
虽然转了之后并没有快多少