prufer编码 与 bzoj1430小猴打架 ——by lethalboy

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34564984/article/details/53032488

Prufer数列是无根树的一种数列。在组合数学中,Prufer数列由有一个对于顶点标过号

的树转化来的数列,点数为n的树转化来的Prufer数列长度为n-2。它可以通过简单的

迭代方法计算出来。它由Heinz Prufer于1918年在证明cayley定理时首次提出。


将树转化成Prufer数列的方法

一种生成Prufer序列的方法是迭代删点,直到原图仅剩两个点。对于一棵顶点已经
经过编号的树T,顶点的编号为{1,2,...,n},在第i步时,移去所有叶子节点(度为1
的顶点)中标号最小的顶点和相连的边,并把与它相邻的点的编号加入Prufer序列
中,重复以上步骤直到原图仅剩2个顶点。

例子:
     以下边的树为例子,首先在所有叶子节点中编号最小的点是
2,和它相邻的点的编号是3,将3加入序列并删除编号为2的
点。接下来删除的点是4,5被加入序列,然后删除5,1被加入
序列,1被删除,3被加入序列,此时原图仅剩两个点(即3和
6),Prufer序列构建完成,为{3,5,1,3}
将Prufer数列转化成树的方法
设{a1,a2,..an-2}为一棵有n个节点的树的Prufer序列,另建一个集合G含有元素{1..n},
找出集合中最小的未在Prufer序列中出现过的数,将该点与Prufer序列中首项连一条
边,并将该点和Prufer序列首项删除,重复操作n-2次,将集合中剩余的两个点之间连
边即可。

例子:
仍为上面的树,Prufer序列为{3,5,1,3},开始时G={1,2,3,4,5,6},未出现的编号最小的
点是2,将2和3连边,并删去Prufer序列首项和G中的2。接下来连的边为{4,5},{1,5},
{1,3},此时集合G中仅剩3和6,在3和6之间连边,原树恢复。

再看看Cayley公式:

Cayley公式是说,一个完全图K_n有n^(n-2)棵生成树,换句话说n个节点的带标


号的无根树有n^(n-2)个。


下面看看bzoj1430:

1430: 小猴打架

Time Limit: 5 Sec  Memory Limit: 162 MB
Submit: 542  Solved: 389
[Submit][Status][Discuss]

Description

一开始森林里面有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}六种不同的打架过程。

Input

一个整数N。

Output

一行,方案数mod 9999991。

Sample Input

4

Sample Output

96

HINT

50%的数据N<=10^3。
100%的数据N<=10^6。


N-1次打架之后形成连通图,实际上可以看做将N-1条边接入图中使其形成树,可以将树化为唯一对应的prufer编码,那么,树的个数实际就是prufer编码的的数目。
由上文中转化prufer编码的方式可知,对于一棵N节点树来说,其prufer编码有N-2位,而生成树的个数就是prufer的个数。那么,就是N^(N-2)个,而加边顺序有(N-1)!种,根据乘法原理,则共有(N-1)!*(N^(N-2))个,这即为答案。

附AC代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<climits>
#define mod 9999991
using namespace std;
typedef long long ll;
int n;ll tmp=1,txp=1;
int main()
{
	scanf("%d",&n);
	for(ll i=1;i<=n-2;i++)tmp=tmp*i%mod,txp=txp*n%mod;
	tmp=tmp*(n-1)%mod;printf("%lld\n",tmp*txp%mod);
}


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页