用户工具

站点工具


2020-2021:teams:legal_string:lgwza:生成函数理论_5_一些例子

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
2020-2021:teams:legal_string:lgwza:生成函数理论_5_一些例子 [2021/02/21 21:05]
lgwza
2020-2021:teams:legal_string:lgwza:生成函数理论_5_一些例子 [2021/02/21 21:08] (当前版本)
lgwza
行 402: 行 402:
  Poly::​Ln(b,​a,​n+1);​  Poly::​Ln(b,​a,​n+1);​
  printf("​%d",​1ll*a[n]*fact[n]%Mod);​  printf("​%d",​1ll*a[n]*fact[n]%Mod);​
 + return 0;
 +}
 +</​code>​
 +</​hidden>​
 +
 +=== 拓展 ===
 +
 +令 $a_n$ 表示在一个 n-集合上完成某个任务的方法数,$a_0=0$。对于 $n\ge 1$,令 $b_n$ 表示将集合 $[n]$ 划分成任意的非空子集,然后在这些非空子集上完成前面任务的方法数,$b_0=1$。设 $\{a_n\}_{n=0}^{\infty}$ 和 $\{b_n\}_{n=0}^{\infty}$ 的指数型生成函数分别为 $f(x)$ 和 $g(x)$,则对任意固定的正整数 $i$,将 $[n]$ 划分成 $i$ 个非空子集时,所对应的方法数的指数型生成函数为 $f^i(x)/​i!$,且有 $$
 +g(x)=e^{f(x)}.
 +$$
 +
 +==== 例 5 ====
 +
 +=== 题意 ===
 +
 +[[https://​www.luogu.com.cn/​problem/​CF438E|CF438E The Child and Binary Tree]]
 +
 +给定一个含有 $n$ 个互异正整数的数列 $c_1,​c_2,​\cdots,​c_n$,如果一棵带点权的有根二叉树满足其所有顶点的权值都在集合 $\{c_1,​c_2,​\cdots,​c_n\}$ 中,则称之为好树,一棵带点权的树的权值是其所有顶点权值的总和。给出一个正整数 $m$,对任意的 $s,1\leq s\leq m$,计算出权值为 $s$ 的好树个数。
 +
 +=== 题解 ===
 +
 +设 $f_i$ 表示点权和为 $i$ 的符合条件的二叉树的个数,$g_i$ 表示权值 $i$ 是否在 $c_i$ 中。 $$
 +f_0=1\\
 +f_n=\sum_{i=1}^ng_i\sum_{j=1}^{n-i}f_jf_{n-i-j}~~(n>​0)
 +$$ 这个式子的意思是枚举根结点的权值 $i$,然后枚举根结点左右子树的权值,容易发现这是三个多项式的卷积。用 $F(x),G(x)$ 分别表示数列 $\{f_n\}_{n=0}^{\infty},​\{g_n\}_{n=0}^{\infty}$ 的普通生成函数。则有 $$
 +F(x)=G(x)F^2(x)+1
 +$$ 解得 $$
 +F(x)=\frac{2}{1+\sqrt{1-4G(x)}}
 +$$ 所以套用多项式开根+求逆即可
 +
 +=== 代码 ===
 +
 +<​hidden>​
 +<code cpp>
 +#​include<​bits/​stdc++.h>​
 +#define _for(i,a,b) for(int i=(a);​i<​(b);​++i)
 +#define _rep(i,a,b) for(int i=(a);​i<​=(b);​++i)
 +using namespace std;
 +const int MAXN=1e5+5,​Mod=998244353;​
 +int n;
 +int g[MAXN<<​2],​f[MAXN<<​2];​
 +int h[MAXN<<​2];​
 +int quick_pow(int x,int y){
 + int ret=1;
 + for(;​y;​y>>​=1,​x=1ll*x*x%Mod)
 + if(y&​1) ret=1ll*ret*x%Mod;​
 + return ret;
 +}
 +unordered_map<​int,​int>​H;​
 +int bsgs(int a,int b){
 + H.clear();
 + int m=sqrt(Mod)+1,​t=b,​base;​
 + for(int i=1;​i<​=m;​i++){
 + t=1ll*t*a%Mod;​
 + H[t]=i;
 + }
 + t=1,​base=quick_pow(a,​m);​
 + for(int i=1;​i<​=m;​i++){
 + t=1ll*t*base%Mod;​
 + if(H[t]) return m*i-H[t];
 + }
 + return -1;
 +}
 +namespace Poly{
 + const int G=3;
 + int rev[MAXN<<​2],​Pool[MAXN<<​3],​*Wn[30];​
 + void init(){
 + int lg2=0,​*pos=Pool,​n,​w;​
 + while((1<<​lg2)<​MAXN*2)lg2++;​
 + n=1<<​lg2,​w=quick_pow(G,​(Mod-1)/​(1<<​lg2));​
 + while(~lg2){
 + Wn[lg2]=pos,​pos+=n;​
 + Wn[lg2][0]=1;​
 + _for(i,​1,​n)Wn[lg2][i]=1LL*Wn[lg2][i-1]*w%Mod;​
 + w=1LL*w*w%Mod;​
 + lg2--;​n>>​=1;​
 + }
 + }
 + int build(int k){
 + int n,pos=0;
 + while((1<<​pos)<​=k)pos++;​
 + n=1<<​pos;​
 + _for(i,​0,​n)rev[i]=(rev[i>>​1]>>​1)|((i&​1)<<​(pos-1));​
 + return n;
 + }
 + void NTT(int *f,int n,bool type){
 + _for(i,​0,​n)if(i<​rev[i])
 + swap(f[i],​f[rev[i]]);​
 + int t1,t2;
 + for(int i=1,​lg2=1;​i<​n;​i<<​=1,​lg2++){
 + for(int j=0;​j<​n;​j+=(i<<​1)){
 + _for(k,​j,​j+i){
 + t1=f[k],​t2=1LL*Wn[lg2][k-j]*f[k+i]%Mod;​
 + f[k]=(t1+t2)%Mod,​f[k+i]=(t1-t2)%Mod;​
 + }
 + }
 + }
 + if(!type){
 + reverse(f+1,​f+n);​
 + int div=quick_pow(n,​Mod-2);​
 + _for(i,​0,​n)f[i]=(1LL*f[i]*div%Mod+Mod)%Mod;​
 + }
 + }
 + void Mul(int *f,int _n,int *g,int _m,int xmod=0){
 + int n=build(_n+_m-2);​
 + _for(i,​_n,​n)f[i]=0;​_for(i,​_m,​n)g[i]=0;​
 + NTT(f,​n,​true);​NTT(g,​n,​true);​
 + _for(i,​0,​n)f[i]=1LL*f[i]*g[i]%Mod;​
 + NTT(f,​n,​false);​
 + if(xmod)_for(i,​xmod,​n)f[i]=0;​
 + }
 + void Inv(const int *f,int *g,int _n){
 + static int temp[MAXN<<​2];​
 + if(_n==1)return g[0]=quick_pow(f[0],​Mod-2),​void();​
 + Inv(f,​g,​(_n+1)>>​1);​
 + int n=build((_n-1)<<​1);​
 + _for(i,​(_n+1)>>​1,​n)g[i]=0;​
 + _for(i,​0,​_n)temp[i]=f[i];​_for(i,​_n,​n)temp[i]=0;​
 + NTT(g,​n,​true);​NTT(temp,​n,​true);​
 + _for(i,​0,​n)g[i]=(2-1LL*temp[i]*g[i]%Mod)*g[i]%Mod;​
 + NTT(g,​n,​false);​
 + _for(i,​_n,​n)g[i]=0;​
 + }
 + void Div(const int *f,int _n,const int *g,int _m,int *q,int *r){
 + static int temp[MAXN<<​2];​
 + if(_n<​_m){
 + _rep(i,​0,​n)r[i]=f[i];​
 + return;
 + }
 + _rep(i,​0,​_m)temp[i]=g[_m-i];​
 + Inv(temp,​q,​_n-_m+1);​
 + _rep(i,​0,​_n)temp[i]=f[_n-i];​
 + Mul(q,​_n-_m+1,​temp,​_n+1,​_n-_m+1);​
 + for(int i=0,​j=_n-_m;​i<​j;​i++,​j--)swap(q[i],​q[j]);​
 + int __m=min(_n-_m+1,​_m);​
 + _for(i,​0,​_m)r[i]=g[i];​_for(i,​0,​__m)temp[i]=q[i];​
 + Mul(r,​_m,​temp,​__m,​_m);​
 + _for(i,​0,​_m)r[i]=(f[i]+Mod-r[i])%Mod;​
 + }
 + void Ln(const int *f,int *g,int _n){
 + static int temp[MAXN<<​2];​
 + Inv(f,​g,​_n);​
 + _for(i,​1,​_n)temp[i-1]=1LL*f[i]*i%Mod;​
 + temp[_n-1]=0;​
 + Mul(g,​_n,​temp,​_n-1,​_n);​
 + for(int i=_n-1;​i;​i--)g[i]=1LL*g[i-1]*quick_pow(i,​Mod-2)%Mod;​
 + g[0]=0;
 + }
 + void Exp(const int *f,int *g,int _n){
 + static int temp[MAXN<<​2];​
 + if(_n==1)return g[0]=1,​void();​
 + Exp(f,​g,​(_n+1)>>​1);​
 + _for(i,​(_n+1)>>​1,​_n)g[i]=0;​
 + Ln(g,​temp,​_n);​
 + temp[0]=(1+f[0]-temp[0])%Mod;​
 + _for(i,​1,​_n)temp[i]=(f[i]-temp[i])%Mod;​
 + Mul(g,​(_n+1)>>​1,​temp,​_n,​_n);​
 + }
 + void Pow(const int *f,int *g,int _n,int k1,int k2){
 + static int temp[MAXN<<​2];​
 + int pos=0,posv;
 + while(!f[pos]&&​pos<​_n)pos++;​
 + if(1LL*pos*k2>​=_n){
 + _for(i,​0,​_n)g[i]=0;​
 + return;
 + }
 + posv=quick_pow(f[pos],​Mod-2);​
 + _for(i,​pos,​_n)g[i-pos]=1LL*f[i]*posv%Mod;​
 + _for(i,​_n-pos,​_n)g[i]=0;​
 + Ln(g,​temp,​_n);​
 + _for(i,​0,​_n)temp[i]=1LL*temp[i]*k1%Mod;​
 + Exp(temp,​g,​_n);​
 + pos=pos*k2,​posv=quick_pow(posv,​1LL*k2*(Mod-2)%(Mod-1));​
 + for(int i=_n-1;​i>​=pos;​i--)g[i]=1LL*g[i-pos]*posv%Mod;​
 + _for(i,​0,​pos)g[i]=0;​
 + }
 + void Sqrt(const int *f,int *g,int _n){
 + static int temp1[MAXN<<​2],​temp2[MAXN<<​2];​
 + if(_n==1) return g[0]=quick_pow(G,​bsgs(3,​f[0])/​2),​void();​
 + Sqrt(f,​g,​(_n+1)>>​1);​
 + _for(i,​(_n+1)>>​1,​_n) g[i]=0;
 + _for(i,​0,​_n) temp1[i]=f[i];​
 + Inv(g,​temp2,​_n);​
 + Mul(temp1,​_n,​temp2,​_n);​
 + int div2=quick_pow(2,​Mod-2);​
 + _for(i,​0,​_n) g[i]=1LL*(g[i]+temp1[i])*div2%Mod;​
 + }
 +}
 +int main(){
 + int n,m;
 + scanf("​%d%d",&​n,&​m);​
 + for(int i=1;​i<​=n;​i++){
 + int x;
 + scanf("​%d",&​x);​
 + g[x]=(Mod-4)%Mod;​
 + }
 + g[0]=1;
 + Poly::​init();​
 + Poly::​Sqrt(g,​h,​m+1);​
 + h[0]=(h[0]+1)%Mod;​
 + Poly::​Inv(h,​f,​m+1);​
 + for(int i=1;​i<​=m;​i++) printf("​%d\n",​1ll*f[i]*2%Mod);​
  return 0;  return 0;
 } }
 </​code>​ </​code>​
 </​hidden>​ </​hidden>​
2020-2021/teams/legal_string/lgwza/生成函数理论_5_一些例子.1613912758.txt.gz · 最后更改: 2021/02/21 21:05 由 lgwza