Prufer序列:
一种将带标号的树用一个唯一的整数序列表示的方法。
prufer序列的构造:
每次将树中编号最小的叶子节点删除,将与它连接的点(显然唯一)加入序列。
n-2次操作之后,树中只剩下两个节点,操作结束,得到prufer序列。
对树构造prufer序列的两种做法:
1.堆,存入所有叶子,每次取出编号最小的叶子。复杂度带log。
2.指针,指向编号最小的叶子,删除之后如果产生新叶子且编号更小则继续删,否则指针++,直到找到下一个叶子节点。复杂度线性。
prufer序列还原树:
根据prufer序列,可以得到树上每个点的度数,知道哪些是叶子。
也可以得到度数最小的叶子的编号,这个叶子一定与prufer序列的第一个数连接。
还原这两个点,然后减少这两个点的度数,重复操作即可。
1.堆做法:编号最小的叶子用堆来找,每次操作减少度数如果新增了叶子,也加入堆中。复杂度带log。
2.指针:产生新叶子的时候,判断这个叶子与指针的大小关系,与构造prufer序列类似。复杂度线性。
性质:
1.prufer序列和树一一对应。
2.大小为n的树,prufer序列的长度为n-2。树中剩下的两个节点,其中一个一定是编号最大的点n。
3.树中每个节点在prufer序列中的出现次数为该点的度数-1
加边使图连通的方案数:
n个点m条边,k个连通块,添加k-1条边使得图连通,求加边方案数。
结论:
nk-2
其中k表示连通块数量,si表示每个连通块点的个数(特判一下k=1的情况,这时候不用乘点数)
Cayley公式:
n个点的完全图,生成树有nn-2种
简单证明:
根据prufer序列,任意长度为n-2,值域为[1,n]的序列都可以唯一对应一棵树。
根据乘法原理,序列的种类有nn-2种,因此方案数为nn-2。
UVA10843 Anne’s game
题意:
给定n,表示有一个n个点的完全图,问有多少种生成树
数据范围:n<=100
解法:
根据cayley公式,方案数为nn-2,直接计算即可
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
const int mod=2000000011;
int ppow(int a,int b,int mod){
int ans=1%mod;a%=mod;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
signed main(){
int T;cin>>T;
for(int cas=1;cas<=T;cas++){
int x;cin>>x;
cout<<"Case #"<<cas<<": ";
if(x<=2)cout<<1<<endl;
else cout<<ppow(x,x-2,mod)<<endl;
}
return 0;
}
Codeforces156 D. Clues
题意:
给定n个点m条边的无向图,要求加入最少的边使得图连通
求加边方案数,答案对k取模
数据范围:n,m<=1e5,k<=1e9
解法:
加边使得图连通的方案数,套结论就行了:
nk-2
其中k表示连通块数量,si表示每个连通块点的个数(特判一下k=1的情况,这时候不用乘点数)
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
int pre[maxm];
int num[maxm];
int n,m,mod;
int ppow(int a,int b,int mod){
int ans=1%mod;a%=mod;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int ffind(int x){
return pre[x]==x?x:pre[x]=ffind(pre[x]);
}
signed main(){
cin>>n>>m>>mod;
for(int i=1;i<=n;i++){
pre[i]=i;
num[i]=1;
}
for(int i=1;i<=m;i++){
int a,b;cin>>a>>b;
int x=ffind(a),y=ffind(b);
if(x!=y){
pre[x]=y;
num[y]+=num[x];
}
}
int cnt=0;
for(int i=1;i<=n;i++){
if(pre[i]==i){
cnt++;
}
}
if(cnt==1){
cout<<1%mod<<endl;//1也要取模,因为模数可能为1
}else{
int ans=ppow(n,cnt-2,mod);
for(int i=1;i<=n;i++){
if(pre[i]==i){
ans=ans*num[i]%mod;
}
}
cout<<ans<<endl;
}
return 0;
}