之前接触过一张给定的图进行生成树计数----利用Matrix-Tree定理转化成kirchhoff矩阵,求n-1阶主子式的值即是答案。
那如果遇到给你一些点,直接问你生成树方案呢?
我考虑过这样做:把所有点之间连边,然后枚举每个点不同的度数(度数之和2n-2),求组成的所有kirchhoff矩阵主子式的和,但是当n足够大,比如超过100000,枚举的复杂度已经相当高了,还要n^3计算行列式,无疑是不可能做到的。
所以,要学习一种新的方法,purfer编码。
参考资料:http://baike.baidu.com/link?url=vkm67NDWUNliaTZF7ZgEJzpks13y1PS_CpY7Y_PQscPRa42vOHQVNtyq77IV5iogJXuTzP-4y40QhpGTHoxZd_ 百度百科(这个不详细)
参考资料:(推荐)http://www.cnblogs.com/zhj5chengfeng/archive/2013/08/23/3278557.html
简单点来说,一棵树唯一对应一个purfer编码,一个purfer编码唯一对应一棵树。
编码:每次把度数为1的标号最小的点删掉,输出与它相连的点,直到只剩2个点
还原:把prufer编码中所有节点编号出现的次数加到每个点的度数里面去(初始度数为1),每次取出度数为1的最小的点和当前purfer所在那一位编号相连,并将两个点度数-1.执行n-2次。最后把剩下的两个度数为1的点相连。
已经知道一棵树唯一对应一个purfer编码了,生成树计数问题就转化成求一棵树的purfer编码有多少种,这个可以运用排列组合的知识,然后就可以通过数论知识快速求解了!
入手例题:
nyoj127 :http://acm.nyist.net/JudgeOnline/problem.php?pid=127
大意是给你n个点,求生成树的个数。n<=1000000(结果对10003取mod)
是一道purfer编码的裸题~
我们考虑把n个点的树变成n-2的purfer编码序列,n-2的每个位置都可以填1~n的编号(没有度数限制),所以purfer编码一共有n^(n-2)种可能,即对应n^(n-2)棵树。
然后就是裸的快速幂取模.
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<cmath>
using namespace std;
const int M=10003;
/*
运用purfer编码的性质,每棵树一定会对应唯一一种n-2长度的purfer sequence
这里不要求度数限制,所以n-2的每个位置有n种选择,答案为n^(n-2) mod M
*/
int T;
int n;
int qk(int x,int y)
{
int res=1;
while(y)
{
if(y&1)res=((res%M)*(x%M))%M;
y>>=1;
x=((x%M)*(x%M))%M;
}
return res;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
printf("%d\n",qk(n,n-2));
}
return 0;
}
PS:这个知识不是单纯的看到的,而是为bzoj1005明明的烦恼做知识储备!!!
再PS:明明的烦恼一开始想用枚举每个点的度数求kirchhoff矩阵主子式之和,大致算了一下复杂度发现无法接受,没办法就上网找题解,然后一大片全是purfer编码,于是就来搞一搞这个鬼东西。