【LOJ6044】「雅礼集训 2017 Day8」共(prufer序列)
- 求有多少棵\(n\)个点的、以\(1\)号点为根的有根树,满足恰好有\(k\)个点深度为奇数(\(1\)号点深度为\(1\))。
- \(n\le5\times10^5\),当\(n>10^3\)时模数为\(998244353\)
\(prufer\)序列的经典应用
首先有一个组合数\(C_{n-1}^{k-1}\),表示从除\(1\)之外的点中选出剩余的深度为奇数的点的方案数。
由于只可能奇数度数和偶数度数的点之间存在边,且每对奇数度数和偶数度数的点之间都可能存在边,因此我们现在有一张两边点数分别是\(k\)和\(n-k\)的完全二分图。
然后就可以借鉴【BZOJ4766】文艺计算姬的结论,这样一张图中的生成树个数就是\(k^{n-k-1}\cdot(n-k)^{k-1}\)。
代码:\(O(n^2)/O(n)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define BN 1000
using namespace std;
int n,k,X;I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
I int Fac(RI x) {RI t=1;W(x) t=1LL*t*(x--)%X;return t;}//暴力算阶乘
int c[BN+5][BN+5];I int C(CI x,CI y)//组合数
{
if(x<=BN) {for(RI i=c[0][0]=1;i<=x;++i) for(RI j=c[i][0]=1;j<=i;++j) c[i][j]=(c[i-1][j-1]+c[i-1][j])%X;return c[x][y];}//暴力求组合数
else return 1LL*Fac(x)*QP(1LL*Fac(y)*Fac(x-y)%X,X-2)%X;//x>BN时必定有逆元,套用组合数计算公式
}
int main()
{
return scanf("%d%d%d",&n,&k,&X),printf("%d\n",1LL*C(n-1,k-1)*QP(k,n-k-1)%X*QP(n-k,k-1)%X),0;//选点方案×建树方案
}
待到再迷茫时回头望,所有脚印会发出光芒