前置知識html
求 \(\left(\sum_{k=0}^{n}f(k)\times x^k\times \binom{n}{k}\right)\bmod p\)ios
把多項式的每一項提出來並用第二類斯特林數展開,數組
\(\begin{aligned} \sum_{k=0}^n\sum_{i=0}^ma_ik^ix^k\binom{n}{k}&=\sum_{k=0}^n \sum_{i=0}^m a_ix^k\binom{n}{k} \sum_{j=0}^i \binom{k}{j}j! S(i,j)\\ &=\sum_{i=0}^m a_i \sum_{j=0}^i S(i,j)j! \sum_{k=0}^n x^k \binom{n}{k}\binom{k}{j} \end{aligned}\)spa
前面已經有一個 \(m^2\) 的枚舉,因此後面的部分須要快速計算,code
可是最後的組合數裏有一個 \(j\) 就比較麻煩,htm
考慮後面兩個組合數的實際意義,從 \(n\) 個元素中選出 \(k\) 個元素,再從 \(k\) 個元素中選出 \(j\) 個元素,實際上就至關於從 \(n\) 個元素中選出 \(j\) 個元素,在從剩下的 \(n-j\) 個元素中選出 \(k-j\) 個元素。blog
\(\begin{aligned} \sum_{i=0}^m a_i \sum_{j=0}^i S(i,j)j! \sum_{k=0}^n x^k \binom{n}{k}\binom{k}{j}&= \sum_{i=0}^m a_i \sum_{j=0}^i S(i,j)j! \sum_{k=0}^n x^k \binom{n}{j}\binom{n-j}{k-j} \\ &=\sum_{i=0}^m a_i \sum_{j=0}^i S(i,j)j!\binom{n}{j} \sum_{k=j}^n x^k \binom{n-j}{k-j}\\ &=\sum_{i=0}^m a_i \sum_{j=0}^i S(i,j)\frac{n!}{(n-j)!} x^j\sum_{k=0}^{n-j} x^k \binom{n-j}{k} \\&= \sum_{i=0}^m a_i \sum_{j=0}^i S(i,j)\frac{n!}{(n-j)!} x^j(x+1)^{n-j} \end{aligned}\)ci
總複雜度 \(m^2 logn\)get
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<vector> #define rg register inline int read(){ rg int x=0,fh=1; rg char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') fh=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*fh; } const int maxn=5e3+5; int n,m,ans,mod,a[maxn],x,f[maxn][maxn]; inline int addmod(rg int now1,rg int now2){ return now1+=now2,now1>=mod?now1-mod:now1; } inline int delmod(rg int now1,rg int now2){ return now1-=now2,now1<0?now1+mod:now1; } inline int mulmod(rg long long now1,rg int now2){ return now1*=now2,now1>=mod?now1%mod:now1; } void pre(){ f[0][0]=1; for(rg int i=1;i<=m;i++){ for(rg int j=1;j<=m;j++){ f[i][j]=addmod(f[i-1][j-1],mulmod(f[i-1][j],j)); } } } int ksm(rg int ds,rg int zs){ rg int nans=1; while(zs){ if(zs&1) nans=mulmod(nans,ds); ds=mulmod(ds,ds); zs>>=1; } return nans; } int main(){ n=read(),x=read(),mod=read(),m=read(); pre(); for(rg int i=0;i<=m;i++) a[i]=read(); for(rg int i=0;i<=m;i++){ rg int tmp=0,now=1; for(rg int j=0;j<=i;j++){ tmp=addmod(tmp,mulmod(f[i][j],mulmod(now,mulmod(ksm(x,j),ksm(x+1,n-j))))); now=mulmod(now,n-j); } ans=addmod(ans,mulmod(a[i],tmp)); } printf("%d\n",ans); return 0; }
給定兩個排列 \(p,q\),可是其中有些位置未知,用 \(0\) 表示。string
如今讓你補全兩個排列,定義兩個排列 \(p,q\) 之間的距離爲每次選擇 \(p\) 中兩個元素交換,使其變成 \(q\) 的最小次數。
如今你需求出對於 \(i \in [0,n-1]\) 求出補全後類似度爲 \(i\) 的方案數。
若是 \(p,q\) 中的元素是已知的,那麼咱們只須要對於每個 \(i\) 由 \(p_i\) 向 \(q_i\)連邊,假設一共造成了 \(cnt\) 個環,那麼最終的答案就是 \(n-cnt\)。
如今元素是不肯定的,仍然按照上面的方式連邊,那麼除了環之外最終會造成四種鏈\((0,x)(x,0)(0,0)(x,y)\)。
設這些鏈分別有有 \(c_{01},c_{10},c_{00},c_{11}\) 個。
對於最後一種鏈由於 \(x\) 和 \(y\) 只出現了一次,因此能夠把 \(x\) 和 \(y\) 當作同一個數,忽略這一條鏈,答案是不影響的。
對於第一種鏈,設 \(f[i]\) 爲用這些 \((0,x)\) 鏈剛好造成 \(i\) 個環的方案數。
直接求很差求,考慮使用二項式反演,設 \(g[i]\) 爲用這些 \((0,x)\) 鏈至少造成 \(i\) 個環的方案數。
那麼 \(g[k]=\sum_{i=k}^{c_{01}} \binom{c_{01}}{i}s(i,j)(c_{00}+c_{01}-i)^{\underline{c_{01}-i}}\)
含義就是選出 \(i\) 條鏈構成 \(k\) 個環,這 \(k\) 個環只由 \((0,x)\) 鏈構成。
剩下的 \((0,x)\) 隨意組合,能夠和 \((0,x)\) 相接,也能夠和 \((0,0)\) 相接,不用考慮是否是造成環。
若是和 \((0,x)\) 相接,最終獲得的仍是 \((0,x)\),若是和 \((0,0)\) 相接,獲得的是 \((0,0)\),可是 \((0,0)\) 的數量加一減一併無受到影響。
不管怎麼接 \((0,0)\) 的數量都不變。
反演一下就能獲得 \(f\) 數組的答案。
對於第二種鏈同理。
對於第三種鏈,設 \(r[i]\) 爲用這些 \((0,0)\) 鏈剛好造成 \(i\) 個環的方案數,
那麼 \(r[i]=s(c_{00},i) c_{00}!\)。
乘上階乘的含義是邊能夠任意排列。
最終的答案就是對這三個數組跑一遍揹包,直接跑是三次方的,能夠先跑前兩個再跑最後一個。
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<vector> #define rg register inline int read(){ rg int x=0,fh=1; rg char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') fh=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*fh; } const int maxn=2e3+5,mod=998244353; inline int addmod(rg int now1,rg int now2){ return now1+=now2,now1>=mod?now1-mod:now1; } inline int delmod(rg int now1,rg int now2){ return now1-=now2,now1<0?now1+mod:now1; } inline int mulmod(rg long long now1,rg int now2){ return now1*=now2,now1>=mod?now1%mod:now1; } int n,a[maxn],b[maxn],C[maxn][maxn],s[maxn][maxn],A[maxn][maxn],jc[maxn],in[maxn],out[maxn],c00,c10,c01,cir; bool vis[maxn]; void pre(){ for(rg int i=1;i<=n;i++) in[i]=out[i]=-1; for(rg int i=1;i<=n;i++) out[a[i]]=b[i],in[b[i]]=a[i]; for(rg int i=1;i<=n;i++){ if(out[i]!=-1 && in[i]==-1){ rg int now=i; while(now && out[now]!=-1) now=out[now]; if(!now) c10++; } } for(rg int i=1;i<=n;i++){ if(in[i]!=-1 && out[i]==-1){ rg int now=i; while(now && in[now]!=-1) now=in[now]; if(!now) c01++; } } for(rg int i=1;i<=n;i++) if(a[i]==0 && b[i]==0) c00++; for(rg int i=1;i<=n;i++){ if(in[i]==0){ rg int now=i; while(now && out[now]!=-1) now=out[now]; if(!now) c00++; } } for(rg int i=1;i<=n;i++){ if(out[i]>0 && !vis[i]){ rg int now=out[i]; vis[now]=1; while(now!=i && now && out[now]) now=out[now],vis[now]=1; if(now==i) cir++; } } } int f1[maxn],f2[maxn],g1[maxn],g2[maxn],f3[maxn],ans[maxn],tmp[maxn]; int main(){ n=read(); for(rg int i=1;i<=n;i++) a[i]=read(); for(rg int i=1;i<=n;i++) b[i]=read(); pre(); s[0][0]=1; for(rg int i=1;i<maxn;i++){ for(rg int j=1;j<maxn;j++){ s[i][j]=addmod(s[i-1][j-1],mulmod(i-1,s[i-1][j])); } } C[0][0]=1; for(rg int i=1;i<maxn;i++) C[i][0]=1; for(rg int i=1;i<maxn;i++){ for(rg int j=1;j<maxn;j++){ C[i][j]=addmod(C[i-1][j],C[i-1][j-1]); } } jc[0]=1; for(rg int i=1;i<maxn;i++) jc[i]=mulmod(jc[i-1],i); for(rg int i=0;i<maxn;i++){ for(rg int j=0;j<maxn;j++){ A[i][j]=mulmod(C[i][j],jc[j]); } } for(rg int i=0;i<=c01;i++){ for(rg int j=i;j<=c01;j++){ g1[i]=addmod(g1[i],mulmod(C[c01][j],mulmod(s[j][i],A[c00+c01-j][c01-j]))); } } for(rg int i=0;i<=c10;i++){ for(rg int j=i;j<=c10;j++){ g2[i]=addmod(g2[i],mulmod(C[c10][j],mulmod(s[j][i],A[c00+c10-j][c10-j]))); } } for(rg int i=0;i<=c01;i++){ for(rg int j=i;j<=c01;j++){ if((j-i)&1) f1[i]=delmod(f1[i],mulmod(C[j][i],g1[j])); else f1[i]=addmod(f1[i],mulmod(C[j][i],g1[j])); } } for(rg int i=0;i<=c10;i++){ for(rg int j=i;j<=c10;j++){ if((j-i)&1) f2[i]=delmod(f2[i],mulmod(C[j][i],g2[j])); else f2[i]=addmod(f2[i],mulmod(C[j][i],g2[j])); } } for(rg int i=0;i<=c00;i++){ f3[i]=mulmod(s[c00][i],jc[c00]); } for(rg int i=0;i<=c01;i++){ for(rg int j=0;j<=c10;j++){ tmp[i+j]=addmod(tmp[i+j],mulmod(f1[i],f2[j])); } } for(rg int i=0;i<=c01+c10;i++){ for(rg int j=0;j<=c00;j++){ ans[i+j]=addmod(ans[i+j],mulmod(tmp[i],f3[j])); } } for(rg int i=0;i<n;i++){ if(n-i<cir) printf("0 "); else printf("%d ",ans[n-i-cir]); } printf("\n"); return 0; }
給定一顆樹,設 \(S(i) = \sum_{j = 1}^{n}{\rm dist}(i, j) ^ k\)。
對於 \(1 \sim n\) 中的每個 \(i\),求出 \(S(i)\) 的值。
若是沒有 \(k\) 次方就是換根 \(dp\) 的模板題。
有了 \(k\) 次方以後,咱們要作的就是怎麼快速由 \(\sum_{i=1}^n dis(i,x)^k\) 轉移到 \(\sum_{i=1}^n (dis(i,x)+1)^k\)
對於後面的部分用二項式定理展開:
\(\sum_{i=1}^n (dis(i,x)+1)^k=\sum_{i=1}^n \sum_{j=0}^k dis(i,x)^j \binom{k}{j}=\sum_{j=0}^k \binom{k}{j} \sum_{i=1}^ndis(i,x)^j\)
記錄 \(dis^0\) 到 \(dis^k\) 的和,先求出以 \(1\) 爲根的答案,再用換根 \(dp\) 求出以其它節點爲根的答案,就能夠作到 \(nk^2\) 的複雜度,能拿到 \(50\) 分。
更快速的作法是用第二類斯特林數進行展開,
\(\begin{aligned} ans_x&=\sum_{i=1}^ndis(i,x)^k \\ &=\sum_{i=1}^n\sum_{j=0}^kS(k,j)C_{dis(i,x)}^jj! \\ &=\sum\limits_{j=0}^kS(k,j)j!\sum\limits_{i=1}^nC_{dis(i,x)}^j \\ &=\sum\limits_{j=0}^kS(k,j)j!\sum\limits_{i=1}^n(C_{dis(i,x)-1}^j+C_{dis(i,x)-1}^{j-1}) \end{aligned}\)
設 \(f[x][j]\) 表示 \(x\) 的子樹內關於 \(C_{dis(i,x)}^j\) 的答案,
那麼 \(f[x][j]=f[son][j]+f[son][j-1]\),
能夠作到 \(nk\) 的複雜度。
#include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<iostream> #include<vector> #include<cstdlib> #include<algorithm> #define rg register inline int read(){ rg int x=0,fh=1; rg char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') fh=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*fh; } const int maxn=1e5+5,maxm=155,mod=10007; int h[maxn],tot=1,n,k; struct asd{ int to,nxt; }b[maxn]; void ad(rg int aa,rg int bb){ b[tot].to=bb; b[tot].nxt=h[aa]; h[aa]=tot++; } int mi[maxn],c[maxm][maxm],f[maxn][maxm],g[maxn][maxm],s[maxm][maxm],jc[maxn]; void dfs1(rg int now,rg int lat){ f[now][0]=1; for(rg int i=h[now];i!=-1;i=b[i].nxt){ rg int u=b[i].to; if(u==lat) continue; dfs1(u,now); for(rg int j=1;j<=k;j++){ f[now][j]+=f[u][j-1]+f[u][j]; f[now][j]%=mod; } f[now][0]+=f[u][0]; f[now][0]%=mod; } } int tmp1[maxm],tmp2[maxm],ans; void dfs2(rg int now,rg int lat){ if(now==1){ for(rg int i=0;i<=k;i++) g[now][i]=f[now][i]; } for(rg int i=h[now];i!=-1;i=b[i].nxt){ rg int u=b[i].to; if(u==lat) continue; for(rg int j=1;j<=k;j++){ tmp2[j]=f[u][j]+f[u][j-1]; tmp2[j]%=mod; } tmp2[0]=f[u][0]; for(rg int j=0;j<=k;j++) tmp1[j]=(g[now][j]-tmp2[j]+mod)%mod; for(rg int j=1;j<=k;j++){ tmp2[j]=tmp1[j]+tmp1[j-1]; tmp2[j]%=mod; } tmp2[0]=tmp1[0]; for(rg int j=0;j<=k;j++){ g[u][j]=f[u][j]+tmp2[j]; g[u][j]%=mod; } dfs2(u,now); } } int main(){ memset(h,-1,sizeof(h)); n=read(),k=read(); rg int aa,bb; for(rg int i=1;i<n;i++){ aa=read(),bb=read(); ad(aa,bb),ad(bb,aa); } for(rg int i=0;i<=k;i++) c[i][0]=1; for(rg int i=1;i<=k;i++){ for(rg int j=1;j<=k;j++){ c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; } } jc[0]=1; for(rg int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mod; s[0][0]=1; for(rg int i=1;i<=k;i++){ for(rg int j=1;j<=i;j++){ s[i][j]=s[i-1][j-1]+s[i-1][j]*j; s[i][j]%=mod; } } dfs1(1,0); dfs2(1,0); for(rg int i=1;i<=n;i++){ ans=0; for(rg int j=0;j<=k;j++){ ans+=s[k][j]*jc[j]%mod*g[i][j]%mod; ans%=mod; } printf("%d\n",ans); } return 0; }
對於一個 \(1,2,\ldots,n\) 的排列,設有 \(A\) 個數的左邊都比它小, \(B\) 個數的右邊都比它小。已知 \(n,A,B\),求知足的排列個數。
設 \(f[i][j]\) 爲前 \(i\) 個建築從左到右有 \(j\) 個建築能看到的方案,
強制讓每一次加的建築高度是最小的,
那麼 \(f[i][j]=f[i-1][j-1]+(i-1)f[i-1][j]\)。
含義是隻有加在最前面的位置才能做出貢獻,不然插在其它 \(i-1\) 個建築的後面都不會做出貢獻。
這個轉移和第一類斯特林數是同樣的,
那麼答案就是:
\(ans=\sum_{i=1}^{n}s(i-1,A-1)s(n-i,B-1)\binom{n-1}{i-1}\)
考慮組合意義,
把 \(n − 1\) 個元素任意塗成黑白色,將黑色元素塞入 \(A − 1\) 個無區別的環,將
白色元素塞入 \(B-1\) 個無區別的環,
等價於:把 \(n − 1\) 個元素塞入 \(A − 1 + B − 1\) 個無區別的環,將其中 \(A − 1\) 個環塗成白色, \(B − 1\) 個塗成黑色。
\(ans=s(n-1,A+B-2)\binom{A+B-2}{A-1}\)
斯特林數直接遞推求便可。
#include<cstdio> #include<cstring> #include<vector> #include<iostream> #include<algorithm> #define rg register inline int read(){ rg int x=0,fh=1; rg char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') fh=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*fh; } const int maxn=5e4+5,maxm=1e6+5,mod=1e9+7; inline int addmod(rg int now1,rg int now2){ return now1+=now2,now1>=mod?now1-mod:now1; } inline int delmod(rg int now1,rg int now2){ return now1-=now2,now1<0?now1+mod:now1; } inline int mulmod(rg long long now1,rg int now2){ return now1*=now2,now1>=mod?now1%mod:now1; } int f[maxn][205],ny[maxm],jc[maxm],jcc[maxm]; void pre(){ f[0][0]=1; for(rg int i=1;i<maxn;i++){ for(rg int j=1;j<=200;j++){ f[i][j]=addmod(f[i-1][j-1],mulmod(f[i-1][j],i-1)); } } ny[1]=1; for(rg int i=2;i<maxn;i++) ny[i]=mulmod(mod-mod/i,ny[mod%i]); jc[0]=jcc[0]=1; for(rg int i=1;i<maxn;i++) jc[i]=mulmod(jc[i-1],i),jcc[i]=mulmod(jcc[i-1],ny[i]); } int getC(rg int nn,rg int mm){ if(nn<mm || nn<0 || mm<0) return 0; return mulmod(jc[nn],mulmod(jcc[mm],jcc[nn-mm])); } int t,n,a,b,ans; int main(){ pre(); t=read(); while(t--){ n=read(),a=read(),b=read(); if(n==1) ans=(a==1 && b==1); else if(a+b<2 || a<0 || b<0) ans=0; else ans=mulmod(f[n-1][a+b-2],getC(a+b-2,a-1)); printf("%d\n",ans); } return 0; }