前置知识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; }