purfer编码解决生成树计数问题

之前接触过一张给定的图进行生成树计数----利用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编码,于是就来搞一搞这个鬼东西。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值