星际之门(一)
时间限制: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
思路:
题意很简单,就是如果有n个点,能生成多少种不同的树,这道题目实际就是考察Purfer序列应用(Purfer序列的博客),一颗n个结点的树,转成一个Purfer序列后,序列的元素有n-2个,而一棵树和一种序列是一一对应的(一棵n个节点的无根树唯一地对应了一个长度为n-2的数列),因此要问能产生多少树,只要看序列的种类数,序列有n-2个位置,每个位置上 的元素有n种(序列中是允许有重复元素的),因此序列的种类即不同的树的种类有种。如果明白这一步后,那么剩下的就是单纯考察一个快速幂取模了。
Cayley定理:有n个节点的完全图的生成树的数量是,或者说n个节点的带标号的无根树有个。
Prufer编码:给定一棵带标号的无根树,找出编号最小的叶子节点,写下与它相邻的节点的编号,然后删掉这个叶子节点。反复执行这个操作直到只剩两个节点为止。
一个有趣的推广是,n个节点的度依次为D1, D2, …, Dn的无根树共有(n-2)! / [ (D1-1)!(D2-1)!..(Dn-1)! ]个,因为此时Prüfer编码中的数字i恰好出现Di-1次。(转自此博客)
ac代码:
#include<stdio.h>
#include<string.h>
#include<queue>
#include<set>
#include<iostream>
#include<map>
#include<stack>
#include<cmath>
#include<algorithm>
#define ll long long
#define mod 10003
#define eps 1e-8
using namespace std;
ll qsm(ll a,ll b)
{
ll t=1;
while(b)
{
if(b&1)
{
t=(t*a)%mod;
}
a=(a*a)%mod;
b>>=1;
}
return t%mod;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
printf("%lld\n",qsm(n,n-2));
}
return 0;
}