多项式学习笔记

P5349 幂 [分治\(fft\)]

\(f(n)=\sum\limits_i a_i x^i\),考虑求\(F(x)=\sum\limits_{n=0}^{\infty} n^x r^n\),其中\(F(0)=\frac{1}{1-r}\)

\[(1-r)F(x)=\sum\limits_{n=0}^{\infty} n^x r^n-\sum\limits_{n=0}^{\infty} n^x r^{n+1}=\sum\limits_{n=0}^{\infty} n^x r^n-\sum\limits_{n=1}^{\infty} (n-1)^x r^{n} \]

\[(r-1)F(x)=\sum\limits_{n=1}^{\infty} r^n ((n-1)^x-n^x)=\sum\limits_{n=1}^{\infty} r^n \sum\limits_{i=0}^{x-1} C_{x}^{i} (-1)^{x-i} n^i=\sum\limits_{i=0}^{x-1} C_{x}^{i} (-1)^{x-i} \sum\limits_{n=1}^{\infty} n^i r^n \]

我们发现\(\sum\limits_{n=1}^{\infty} n^i r^n\)几乎就是\(F(i)\),只是在\(i=0\)时它是\(F(0)-1\),考虑先将\(F(0)\)减一。

\[(r-1)F(x)=\sum\limits_{i=0}^{x-1} C_{x}^{i} (-1)^{x-i} F(i)=\sum\limits_{i=0}^{x-1} \frac{x!}{i!(x-i)!} (-1)^{x-i} F(i) \]

\[\frac{F(x)}{x!}=\sum\limits_{i=0}^{x-1} \frac{1}{r-1} \frac{(-1)^{x-i}}{(x-i)!} \frac{F(i)}{i!} \]

分治\(fft\)即可,最后\(F(0)\)要加一,答案即为\(\sum\limits_{i=0}^m a_iF(i)i!\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6+10;
const int mod=998244353;
const int G=3;
const int iG=(mod+1)/3;
const int inv2=(mod+1)/2;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
inline int ksm(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=res*x%mod;
		x=x*x%mod;y>>=1;
	}
	return res;
}
int tr[maxn];
int n,p,ans,a[maxn],b[maxn];
inline void ntt(int *f,int len,int flag){
    for(int i=0;i<len;i++)
        if(i<tr[i])swap(f[i],f[tr[i]]);
    for(int i=2;i<=len;i<<=1){
        int w=ksm(flag?G:iG,(mod-1)/i);
        for(int j=0,p=i>>1;j<len;j+=i){
            int wi=1;
            for(int k=j;k<j+p;k++){
                int tt=1ll*wi*f[k+p]%mod;
                f[k+p]=(f[k]-tt+mod)%mod;
                f[k]=(f[k]+tt)%mod;
                wi=wi*w%mod;
            }
        }
    }
    if(flag==0){
        int iv=ksm(len,mod-2);
        for(int i=0;i<len;i++)
            f[i]=f[i]*iv%mod;
    }
}
int f[maxn],g[maxn];
inline void cdqfft(int l,int r){
	if(l+1>=r)return;
	int mid=(l+r)>>1,len=r-l;
	cdqfft(l,mid);
	for(int i=0;i<len;i++)g[i]=a[i];
	for(int i=l;i<mid;i++)f[i-l]=b[i];
	for(int i=mid;i<r;i++)f[i-l]=0;
	for(int i=0;i<len;i++)
		tr[i]=(tr[i>>1]>>1)|((i&1)?len>>1:0);
	ntt(g,len,1),ntt(f,len,1);
	for(int i=0;i<len;i++)
		f[i]=g[i]*f[i]%mod;
	ntt(f,len,0);
	for(int i=mid;i<r;i++)
		b[i]=(b[i]+f[i-l])%mod;
	cdqfft(mid,r);
}
int inv[maxn],ifc[maxn],fac[maxn];
signed main(){
	n=read()+1,p=read();
	inv[1]=ifc[0]=fac[0]=1;
	for(int i=2;i<=n;i++)
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	for(int i=1;i<=n;i++)
		ifc[i]=ifc[i-1]*inv[i]%mod;
	for(int i=1;i<=n;i++)
		fac[i]=fac[i-1]*i%mod;
	int iv=ksm(p-1,mod-2);
	for(int i=1;i<n;i++)
		a[i]=(i&1?mod-ifc[i]:ifc[i])*iv%mod;
	int len=1;for(;len<n;len<<=1);
	b[0]=ksm(1+mod-p,mod-2)-1;cdqfft(0,len);b[0]++;
	for(int i=0;i<n;i++){
		int x=read();
		ans=(ans+x*b[i]%mod*fac[i]%mod)%mod;
	}
	printf("%lld",ans);
	return 0;
}

P4841 [集训队作业2013]城市规划 [多项式ln]

结论:\(n\)元的命题\(x\)\(\rightarrow\)随意划分\(n\)后每一个子集的命题\(x\),则后者是前者的\(\text{exp}\)

套用结论,\(n\)点无向连通图个数的\(\text{exp}\)\(n\)点无向图个数。

而显然后者的个数是\(2^{C_{n}^2}\),那么多项式\(\text{ln}\)一下即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6+10;
const int mod=1004535809;
const int G=3;
const int iG=(mod+1)/3;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
inline int ksm(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=res*x%mod;
		x=x*x%mod;y>>=1;
	}
	return res;
}
int tr[maxn],inv[maxn];
int n,a[maxn],b[maxn],c[maxn];
inline void ntt(int *f,int len,int flag){
    for(int i=0;i<len;i++)
        if(i<tr[i])swap(f[i],f[tr[i]]);
    for(int i=2;i<=len;i<<=1){
        int w=ksm(flag?G:iG,(mod-1)/i);
        for(int j=0,p=i>>1;j<len;j+=i){
            int wi=1;
            for(int k=j;k<j+p;k++){
                int tt=1ll*wi*f[k+p]%mod;
                f[k+p]=(f[k]-tt+mod)%mod;
                f[k]=(f[k]+tt)%mod;
                wi=wi*w%mod;
            }
        }
    }
    if(flag==0){
        int iv=ksm(len,mod-2);
        for(int i=0;i<len;i++)
            f[i]=f[i]*iv%mod;
    }
}
inline void workinv(int deg,int *a,int *b){
	if(deg==1)return void(b[0]=ksm(a[0],mod-2));
	workinv((deg+1)>>1,a,b);
	int len=1;for(;len<=(deg<<1);len<<=1);
	for(int i=0;i<len;i++)
		tr[i]=(tr[i>>1]>>1)|((i&1)?len>>1:0);
	for(int i=0;i<deg;i++)c[i]=a[i];
	for(int i=deg;i<len;i++)c[i]=0;
	ntt(c,len,1);ntt(b,len,1);
	for(int i=0;i<len;i++)
		b[i]=b[i]*(2-b[i]*c[i]%mod+mod)%mod;
	ntt(b,len,0);
	for(int i=deg;i<len;i++)b[i]=0;
}
inline void dif(int *a,int *b,int len){
	for(int i=0;i<len;i++)
		a[i]=b[i+1]*(i+1)%mod;
}
inline void iit(int *a,int *b,int len){
	inv[1]=1;a[0]=0;
	for(int i=2;i<=len;i++)
		inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	for(int i=1;i<len;i++)
		a[i]=b[i-1]*inv[i]%mod;
}
inline void mul(int *a,int *b,int n){
	int len=1;for(;len<=(n<<1);len<<=1);
	for(int i=0;i<len;i++)
		tr[i]=(tr[i>>1]>>1)|((i&1)?len>>1:0);
	ntt(a,len,1);ntt(b,len,1);
	for(int i=0;i<len;i++)a[i]=a[i]*b[i]%mod;
	ntt(a,len,0);
}
int f[maxn],fac[maxn];
signed main(){
	n=read()+1;
	fac[0]=1;
	for(int i=1;i<=n;i++)
		fac[i]=fac[i-1]*i%mod;
	for(int i=0;i<n;i++)
		a[i]=ksm(2,i*(i-1)/2)*ksm(fac[i],mod-2)%mod;
	dif(f,a,n);
	workinv(n,a,b);
	mul(f,b,n);
	iit(a,f,n);
	printf("%lld\n",a[n-1]*fac[n-1]%mod);
	return 0;
}

CF438E The Child and Binary Tree [多项式开根,多项式求逆]

\(a_i\)代表\(i\)是否在\(c\)中出现过,\(f_i\)代表和为\(i\)的二叉树个数,\(f_0=1\)

\[f_i=\sum\limits_{j=1}^i a_j \sum\limits_{k=0}^{i-j} f_k f_{i-j-k} \]

这几乎就是卷积形式,但可惜\(i=0\)时不符合,但没关系,多项式是可以单独加上尾项的系数的。

\[f=a*(f*f)+1=af^2+1 \]

\[f=\frac{1\pm \sqrt{1-4a}}{2a}=\frac{2}{1\mp \sqrt{1-4a}} \]

经验证,\(f=\frac{2}{1+\sqrt{1-4a}}\),多项式开根然后求逆即可。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6+10;
const int mod=998244353;
const int G=3;
const int iG=(mod+1)/3;
const int inv2=(mod+1)/2;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
inline int ksm(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=res*x%mod;
		x=x*x%mod;y>>=1;
	}
	return res;
}
int tr[maxn];
int n,m,a[maxn],b[maxn],c[maxn];
inline void ntt(int *f,int len,int flag){
    for(int i=0;i<len;i++)
        if(i<tr[i])swap(f[i],f[tr[i]]);
    for(int i=2;i<=len;i<<=1){
        int w=ksm(flag?G:iG,(mod-1)/i);
        for(int j=0,p=i>>1;j<len;j+=i){
            int wi=1;
            for(int k=j;k<j+p;k++){
                int tt=1ll*wi*f[k+p]%mod;
                f[k+p]=(f[k]-tt+mod)%mod;
                f[k]=(f[k]+tt)%mod;
                wi=wi*w%mod;
            }
        }
    }
    if(flag==0){
        int iv=ksm(len,mod-2);
        for(int i=0;i<len;i++)
            f[i]=f[i]*iv%mod;
    }
}
inline void workinv(int deg,int *a,int *b){
	if(deg==1)return void(b[0]=ksm(a[0],mod-2));
	workinv((deg+1)>>1,a,b);
	int len=1;for(;len<=(deg<<1);len<<=1);
	for(int i=0;i<len;i++)
		tr[i]=(tr[i>>1]>>1)|((i&1)?len>>1:0);
	for(int i=0;i<deg;i++)c[i]=a[i];
	for(int i=deg;i<len;i++)c[i]=0;
	ntt(c,len,1);ntt(b,len,1);
	for(int i=0;i<len;i++)
		b[i]=b[i]*(2-b[i]*c[i]%mod+mod)%mod;
	ntt(b,len,0);
	for(int i=deg;i<len;i++)b[i]=0;
}
int f[maxn],g[maxn];
inline void worksqrt(int deg,int *a,int *b){
	if(deg==1)return void(b[0]=1);
	worksqrt((deg+1)>>1,a,b);
	int len=1;for(;len<=(deg<<1);len<<=1);
	for(int i=0;i<len;i++)f[i]=g[i]=0;
	for(int i=0;i<deg;i++)f[i]=a[i];
	workinv(deg,b,g);
	ntt(b,len,1);ntt(f,len,1);ntt(g,len,1);
	for(int i=0;i<len;i++)
		b[i]=inv2*g[i]%mod*(f[i]+b[i]*b[i]%mod)%mod;
	ntt(b,len,0);
	for(int i=deg;i<len;i++)b[i]=0;
}
signed main(){
	n=read(),m=read()+1;
	int x;
	for(int i=0;i<n;i++)
		x=read(),a[x]=1;
	a[0]=1;
	for(int i=1;i<m;i++)
		if(a[i])a[i]=mod-4;
	worksqrt(m,a,b);b[0]++;
	for(int i=0;i<m<<1;i++)
		a[i]=b[i],b[i]=0;
	workinv(m,a,b);
	for(int i=1;i<m;i++)
		printf("%lld ",2*b[i]%mod);
	return 0;
}

P3723 [AH2017/HNOI2017]礼物 [ntt]

\(val=\sum (a_i-b_i+c)^2=\sum (a_i^2+b_i^2)+nc^2+2c\sum (a_i-b_i)+\sum a_ib_i\)

容易发现除了最后一项其他都可以线性求出最值,\(\sum a_ib_i\)直接套路地\(ntt\)即可。

之前的做法是枚举的\(c\),匆匆改过来导致代码有点乱,体谅一下。

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=1e6+10;
const int mod=998244353;
const int G=3;
const int iG=(mod+1)/3;
int n,m,len=1,tr[maxn],a[maxn],b[maxn];
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
inline int ksm(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=1ll*res*x%mod;
		x=1ll*x*x%mod;y>>=1;
	}
	return res;
}
inline void ntt(int *f,int len,int flag){
	for(int i=0;i<len;i++)
		if(i<tr[i])swap(f[i],f[tr[i]]);
	for(int p=2;p<=len;p<<=1){
		int l=p>>1,w=ksm(flag?G:iG,(mod-1)/p);
		for(int i=0;i<len;i+=p){
			int wi=1;
			for(int j=i;j<i+l;j++){
				int t=1ll*wi*f[j+l]%mod;
				f[j+l]=(f[j]-t+mod)%mod;
				f[j]=(f[j]+t)%mod;
				wi=1ll*w*wi%mod;
			}
		}
	}
	if(!flag){
		int iv=ksm(len,mod-2);
		for(int i=0;i<len;i++)
			f[i]=1ll*f[i]*iv%mod;
	}
}
int f[maxn],g[maxn],ans=inf,base=inf;
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=n;i++)b[i]=read();
	for(;len<=(n<<2);len<<=1);
	for(int i=0;i<len;i++)
		tr[i]=(tr[i>>1]>>1)|((i&1)?len>>1:0);
	int s2=0,s1=0;
	for(int i=1;i<=n;i++)
		s2+=a[i]*a[i]+b[i]*b[i],s1+=a[i]-b[i];
	//printf("s1=%d s2=%d\n",s1,s2);
	for(int i=-m;i<=m;i++)
		base=min(base,s2+2*s1*i+n*i*i);
	//printf("base=%d\n",base);
	for(int c=0;c<=0;c++){
		for(int i=0;i<len;i++)f[i]=g[i]=0;
		for(int i=1;i<=n;i++)f[n-i]=(a[i]+c+mod)%mod;
		for(int i=0;i<n;i++)g[i+n]=g[i]=b[i+1];
		//int base=0;for(int i=0;i<n;i++)base+=1ll*f[i]*f[i]%mod+g[i]*g[i];
		ntt(f,len,1),ntt(g,len,1);
		for(int i=0;i<len;i++)f[i]=1ll*f[i]*g[i]%mod;
		ntt(f,len,0);
		for(int i=n-1;i<=2*n-2;i++)
			ans=min(ans,(base-2*f[i]%mod+mod)%mod);
	}
	printf("%d\n",ans);
	return 0;
}

CF891E Lust [EGF]

发现操作的顺序无影响,故套路地设第\(i\)个被减了\(b_i\)次,稍加推导发现结果即为\(\prod a_i-\prod (a_i-b_i)\)

即最终答案为\(\prod a_i -\frac{1}{n^k} \sum \frac{k!}{\prod b_i!} \prod (a_i-b_i)\)(*),漂亮的\(\text{EGF}\)式子,可惜我到这就不会了。

考虑单个的生成函数\(f_p=\sum \frac{a_p-i}{i!} x^i\),发现可以化简,\(f_p=(a_p-x)e^x\),整体即为\(F=e^{nx}\prod (a_i-x)\)

注意到这一坨\(\prod (a_i-x)\)的最大次数只有\(n\),可以\(n^2\)递推出来系数,不妨设为\(\{c_i\}\),即\(\sum c_i x^i\)

根据\(e\)的定义,\(e^{nx}=\sum \frac{(nx)^i}{i!}\),则\([x^k]F=\sum\limits_{i=0}^n c_i \frac{n^{k-i}}{(k-i)!}\),带入*式即为\(c_0-\sum\limits_{i=0}^n\frac{c_i k!}{n^i (k-i)!}\)

暴力计算答案即可,时间复杂度为\(\Theta(n)\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+10;
const int mod=1e9+7;
int n,k,f[maxn],a[maxn],ans,inv;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
inline int ksm(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=res*x%mod;
		x=x*x%mod;y>>=1;
	}
	return res;
}
signed main(){
	n=read(),k=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	f[0]=1;inv=ksm(n,mod-2);
	for(int i=1;i<=n;i++){
		for(int j=i;j;--j)
			f[j]=(a[i]*f[j]%mod-f[j-1]+mod)%mod;
		f[0]=f[0]*a[i]%mod;
	}
	for(int i=0,tmp=1;i<=n&&i<=k;i++){
		ans=(ans+f[i]*tmp%mod)%mod;
		tmp=tmp*(k-i)%mod*inv%mod;
	}
	printf("%lld\n",(f[0]-ans+mod)%mod);
	return 0;
}
posted @ 2021-02-04 21:47  syzf2222  阅读(133)  评论(0编辑  收藏  举报