可以用於處理一類有關生成樹計數類的問題。
prufer序列是有關無根樹的序列,並且n點無根樹能唯一對應n-2長度的prufer序列。
一、prufer序列生成方法:
把無根樹裏所有度數爲1的點定義爲葉子,重複執行以下操作直至原樹裏只剩下兩個節點:選取當前標號最小的葉子,把它的父親接在當前生成的prufer序列末尾,並在原樹裏刪除該點。
二、prufer序列還原無根樹:
設一個點集V={1,2,3,...,n},然後重複執行以下操作,取出prufer序列當前開頭元素x,然後在V中從左到右遍歷找到第一個沒有在當前prufer序列中出現的元素y,並把x、y連一條邊,然後prufer序列刪除開頭x,點集V刪除y。直到V中剩下兩個點時,把這兩個點連一條邊,至此,原樹還原完成。
實現生成還原的代碼可以參看這位大佬博客:https://blog.csdn.net/morejarphone/article/details/50677172
三、性質
1.從上面的生成還原看,prufer序列內每個點取值都可以在[1,n]內,這樣可以證明一個n個點的完全圖生成的無根樹數量爲n^(n-2)
2.可以發現每個點在prufer序列每出現一次就對應了一個與它相連的點的刪除,所以prufer序列中某點出現次數等於該點在原樹上的度數減一。
3.依據上面第二條性質,假設給定樹上每個節點度數,可以發現生成樹個數就是一個含重複元素的集合的排列數,就是階乘除以一堆階乘積的東西...
四、模板題
HNOI2004-樹的計數
鏈接:https://www.luogu.org/problemnew/show/P2290
注意判等於0的一些情況。(度數爲0而點數大於1)
代碼:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=160;
ll ans=1;
int n,d[N],tax[N*N],tnum=0;
int gcd(int x,int y)
{return !y?x:gcd(y,x%y);}
int main()
{
int sum=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&d[i]),--d[i],sum+=d[i];
if(d[i]<0&&n!=1)return puts("0"),0;
for(int j=2;j<=d[i];j++)
tax[++tnum]=j;
}
if(sum!=n-2)return puts("0"),0;
for(int i=2,nw;i<=n-2;i++)
{
nw=i;
for(int j=1,tmp;j<=tnum;j++)
{
if(tax[j]==1)continue;
tmp=gcd(nw,tax[j]);
nw/=tmp,tax[j]/=tmp;
}
ans=ans*nw;
}
printf("%lld\n",ans);
}