BZOJ 1430 小猴打架 - prufer数列
描述
一开始森林里面有N只互不相识的小猴子,它们经常打架,但打架的双方都必须不是好朋友。每次打完架后,打架的双方以及它们的好朋友就会互相认识,成为好朋友。经过$N-1$次打架之后,整个森林的小猴都会成为好朋友。 现在的问题是,总共有多少种不同的打架过程。 比如当$N=3$时,就有{1-2,1-3}{1-2,2-3}{1-3,1-2}{1-3,2-3}{2-3,1-2}{2-3,1-3}六种不同的打架过程。
题解
问题 $=$ 求出$N$个有标号节点的树的个数 $\times$ 连边的顺序。
因为有 $N - 1$条边, 所以连边的顺序就是 $!(N-1)$。
然后需要考虑的就是$N$个有标号节点的树的个数。
我们首先要介绍prufer数列, 传送门
prufer数列的构造方法 :找出度数为1 的节点中标号最小的点$x$,将与它相连的节$y$点录入数列, 再删除$x$。
重复上述过程 , 直到只剩两个点。 这样构造出的数列与树唯一对应。 并且树也唯一对应数列。即数列的个数与树的个数相同。
个数都为$N^{N-2}$
最后的答案就是$N^{N - 2} \times !(N - 1)$
代码
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 const int mod = 9999991; 7 8 ll ans, n; 9 10 ll fpow(ll a,ll p) { 11 ll re = 1; 12 for(; p; p >>= 1, a = a * a % mod) if(p & 1) re = re * a % mod; 13 return re; 14 } 15 16 int main() 17 { 18 scanf("%lld", &n); 19 ans = fpow(n, n - 2); 20 for(int i = 1; i < n; ++i) ans = ans * i % mod; 21 printf("%lld\n", ans); 22 }