nyoj-127 星际之门(一) prufer编码 Cayley公式 的基本应用

星际之门(一)

时间限制: 3000 ms  |  内存限制: 65535 KB
难度: 3
描述

公元3000年,子虚帝国统领着N个星系,原先它们是靠近光束飞船来进行旅行的,近来,X博士发明了星际之门,它利用虫洞技术,一条虫洞可以连通任意的两个星系,使人们不必再待待便可立刻到达目的地。

帝国皇帝认为这种发明很给力,决定用星际之门把自己统治的各个星系连结在一起。

可以证明,修建N-1条虫洞就可以把这N个星系连结起来。

现在,问题来了,皇帝想知道有多少种修建方案可以把这N个星系用N-1条虫洞连结起来?

 

输入
第一行输入一个整数T,表示测试数据的组数(T<=100)
每组测试数据只有一行,该行只有一个整数N,表示有N个星系。(2<=N<=1000000)
输出
对于每组测试数据输出一个整数,表示满足题意的修建的方案的个数。输出结果可能很大,请输出修建方案数对10003取余之后的结果。
样例输入
2

3

4
样例输出
3

16
代码:
View Code
#include<stdio.h>

int main()

{

    int x,n,i,m;

    scanf("%d",&x);

    while(x--)

    {

        scanf("%d",&m);

        n=m;

        for(i=1;i<m-2;i++)

        {

            n*=m;

            n%=10003;

        }

        if(n==2)

            printf("1\n");

        else

            printf("%d\n",n);

    }

    return 0;

}

一看就知道这里面是有规律的,不过推了半天还是一头雾水,感觉像一个树,可是就是找不到 递推公式...推了老半天才发现,可是数据量太大。别人说这是Cayley定理:n^(n-2)

百度里面没有词条,找了一下文章,都是从Matix67那copy的,不过我感觉他写的还是麻烦,有找了个 才弄懂。其实就是先由一个无根树,前推出prufer编码,当我们再由 prufer编码 反推无根树就会发现,编码有n-2个数组成,只要这些数在1到n之间,组成就行了。无根树和编码是一一对应的。

下面是转的较容易懂的版本1:

prufer编码是用另外一种形式来描述一棵树,这棵树是无根树,它可以和无根树之间形成一一对应关系。
编码方式是:

 

prufer编码 - 小子 - 那个小子

这是一颗无根树,这课树的prufer编码为5,5,4,4,4,6。

首 先选这棵树叶子中编号最小的点,将这个点删除,并且把它的邻接点加入一个数组中,例如第一个删除的节点为1,并且把5加入数组中。删除节点后形成一棵新的 树,再在新树中删除最小的节点,并且把邻接点加入数组中,,这样重复以上步骤,知道树中最后剩余两个点的时候终止操作。这时候数组中的便是prufer编 码。

由prufer编码来重建这棵树的方法是:

假如prufer编码为(a1,a2,a3,a4,a5,.....an-2)在上述数组中,在数组最后加入n这个值,这样便形成了数组中包含n-1个节点,例如上述为5,5,4,4,4,6,8。

然后取不在数组中的最小值为b1,则b1与a1是邻接点,在数组中删除a1,再在剩下的数中选取不为b1,且不在数组中的最小值为b2,则b2与a2是邻接点,这样依次循环下去直到结束,这样便形成了一棵树。

Cayley定理:不同的n节点标号树的数量为n^(n-2)。

 

Matrix67版本:

Cayley公式是说,一个完全图K_n有n^(n-2)棵生成树,换句话说n个节点的带标号的无根树有n^(n-2)个。今天我学到了Cayley公式的一个非常简单的证明,证明依赖于Prüfer编码,它是对带标号无根树的一种编码方式。
    给定一棵带标号的无根树,找出编号最小的叶子节点,写下与它相邻的节点的编号,然后删掉这个叶子节点。反复执行这个操作直到只剩两个节点为止。由 于节点数n>2的树总存在叶子节点,因此一棵n个节点的无根树唯一地对应了一个长度为n-2的数列,数列中的每个数都在1到n的范围内。下面我们只 需要说明,任何一个长为n-2、取值范围在1到n之间的数列都唯一地对应了一棵n个节点的无根树,这样我们的带标号无根树就和Prüfer编码之间形成一 一对应的关系,Cayley公式便不证自明了。

    注意到,如果一个节点A不是叶子节点,那么它至少有两条边;但在上述过程结束后,整个图只剩下一条边,因此节点A的至少一个相邻节点被去掉过,节 点A的编号将会在这棵树对应的Prüfer编码中出现。反过来,在Prüfer编码中出现过的数字显然不可能是这棵树(初始时)的叶子。于是我们看到,没 有在Prüfer编码中出现过的数字恰好就是这棵树(初始时)的叶子节点。找出没有出现过的数字中最小的那一个(比如④),它就是与Prüfer编码中第 一个数所标识的节点(比如③)相邻的叶子。接下来,我们递归地考虑后面n-3位编码(别忘了编码总长是n-2):找出除④以外不在后n-3位编码中的最小 的数(左图的例子中是⑦),将它连接到整个编码的第2个数所对应的节点上(例子中还是③)。再接下来,找出除④和⑦以外后n-4位编码中最小的不被包含的 数,做同样的处理……依次把③⑧②⑤⑥与编码中第3、4、5、6、7位所表示的节点相连。最后,我们还有①和⑨没处理过,直接把它们俩连接起来就行了。由 于没处理过的节点数总比剩下的编码长度大2,因此我们总能找到一个最小的没在剩余编码中出现的数,算法总能进行下去。这样,任何一个Prüfer编码都唯 一地对应了一棵无根树,有多少个n-2位的Prüfer编码就有多少个带标号的无根树。

    一个有趣的推广是,n个节点的度依次为D1, D2, ..., Dn的无根树共有(n-2)! / [ (D1-1)!(D2-1)!..(Dn-1)! ]个,因为此时Prüfer编码中的数字i恰好出现Di-1次。

 

你可能感兴趣的:(编码)