这个 \(D\) 题比赛切掉的人基本上是 \(C\) 题的 \(5,6\) 倍...果真数学计数问题比数据结构更受欢迎...c++
广义 \(Cayley\) 定理:git
\(n\) 个标号节点造成一个有 \(k\) 颗树的森林,使得给定的 \(k\) 个点没有两个点属于同一颗树的方案数为\(k\cdot n^{n-k-1}.\)数据结构
证实能够用概括法,对 \(n\) 概括,枚举节点 \(1\) 的邻居便可得递推式,进而得出证实.spa
\[ f(edges)=A(n-2,edges-1) \cdot C(m-1,edges-1)\cdot m^{n-edges-1} \cdot (edges+1) \cdot n^{n-edges-2}. \]翻译
#include<bits/stdc++.h> using namespace std; #define ll long long #define mp make_pair #define pii pair<int,int> inline int read() { int x=0; bool pos=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return pos?x:-x; } const int P=1e9+7; inline int add(int a,int b) { return (a + b) % P; } inline int mul(int a,int b) { return 1LL * a * b % P; } int fpow(int a,int b) { int res=1; while(b) { if(b&1) res=mul(res,a); a=mul(a,a); b>>=1; } return res; } const int MAXN=1e6+10; int fac[MAXN],invfac[MAXN],mpow[MAXN],npow[MAXN]; void init(int n,int m) { int mx=max(n,m); fac[0]=1; for(int i=1;i<=mx;++i) fac[i]=mul(fac[i-1],i); invfac[mx]=fpow(fac[mx],P-2); for(int i=mx-1;i>=0;--i) invfac[i]=mul(invfac[i+1],i+1); mpow[0]=npow[0]=1; for(int i=1;i<=n;++i) mpow[i]=mul(mpow[i-1],m); for(int i=1;i<=n;++i) npow[i]=mul(npow[i-1],n); } int A(int n,int m) { if(n<m || n<0 || m<0) return 0; return mul(fac[n],invfac[n-m]); } int C(int n,int m) { if(n<m || n<0 || m<0) return 0; return mul(fac[n],mul(invfac[n-m],invfac[m])); } int main() { int n=read(),m=read(); int a=read(),b=read(); init(n,m); int ans=0; for(int edges=1;edges<n;++edges) { int tmp=mul(A(n-2,edges-1),C(m-1,edges-1)); tmp=mul(tmp,mpow[n-edges-1]); tmp=mul(tmp,edges==n-1?1:mul(edges+1,npow[n-edges-2])); ans=add(ans,tmp); } printf("%d\n",ans); return 0; }