Prufer序列 、 Cayley公式

本文介绍Prufer序列的概念,包括其构造与还原树的方法,以及如何利用Prufer序列解决加边使图连通的问题。此外,还探讨了Cayley公式在生成树计数中的应用。

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∏i=1ksi\prod _{i=1}^ksii=1ksi
其中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∏i=1ksi\prod _{i=1}^ksii=1ksi
其中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;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值