【BZOJ 1211】 1211: [HNOI2004]樹的計數 (prufer序列、計數)

  

1211: [HNOI2004]樹的計數

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 2468  Solved: 868

Description

一個有n個結點的樹,設它的結點分別爲v1, v2, …, vn,已知第i個結點vi的度數爲di,問知足這樣的條件的不一樣的樹有多少棵。給定n,d1, d2, …, dn,編程須要輸出知足d(vi)=di的樹的個數。ios

Input

第一行是一個正整數n,表示樹有n個結點。第二行有n個數,第i個數表示di,即樹的第i個結點的度數。其中1<=n<=150,輸入數據保證知足條件的樹不超過10^17個。編程

Output

輸出知足條件的樹有多少棵。ide

Sample Input

4
2 1 2 1

Sample Output

2

HINT

Source

 

 

【分析】spa

  無根樹的表示法用prufer數列。【長姿式】code

  

將樹轉化成Prufer數列的方法

一種生成Prufer序列的方法是迭代刪點,直到原圖僅剩兩個點。對於一棵頂點已經通過編號的樹T,頂點的編號爲{1,2,...,n},在第i步時,移去全部葉子節點(度爲1的頂點)中標號最小的頂點和相連的邊,並把與它相鄰的點的編號加入Prufer序列中,重複以上步驟直到原圖僅剩2個頂點。
例子

 

Prufer數列
以上面的樹爲例子,首先在全部葉子節點中編號最小的點是2,和它相鄰的點的編號是3,將3加入序列並刪除編號爲2的點。接下來刪除的點是4,5被加入序列,而後刪除5,1被加入序列,1被刪除,3被加入序列,此時原圖僅剩兩個點(即3和6),Prufer序列構建完成,爲{3,5,1,3}

將Prufer數列轉化成樹的方法

設{a1,a2,..an-2}爲一棵有n個節點的樹的Prufer序列,另建一個集合G含有元素{1..n},找出集合中最小的未在Prufer序列中出現過的數,將該點與Prufer序列中首項連一條邊,並將該點和Prufer序列首項刪除,重複操做n-2次,將集合中剩餘的兩個點之間連邊便可。
例子
仍爲上面的樹,Prufer序列爲{3,5,1,3},開始時G={1,2,3,4,5,6},未出現的編號最小的點是2,將2和3連邊,並刪去Prufer序列首項和G中的2。接下來連的邊爲{4,5},{1,5},{1,3},此時集合G中僅剩3和6,在3和6之間連邊,原樹恢復
 
  說明樹和prufer數列是一一對應的。
  這個數列的特色是:這個點的度數-1=它在數列的出現次數。
  因此數列總長度是n-2。
 
  這道是prufer數列的裸題。
  首先判斷是否無解啦。
  特判n=1,而後d=0,d>=n這種狀況。
  且$\sum d[i]-1 = n-2$要成立。
  而後最後的答案就是$\dfrac{(n-2)!}{\Pi (d[i]-1)!}$
 
  手動消因子便可。最後答案保證不超過long long了。
 
 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 using namespace std;
 7 #define Maxn 160
 8 #define LL long long
 9 
10 int d[Maxn],cnt[Maxn];
11 
12 void cal(int x,int y)
13 {
14     for(int i=2;i*i<=x;i++) if(x%i==0)
15     {
16         while(x%i==0) cnt[i]+=y,x/=i;
17     }
18     if(x!=1) cnt[x]+=y;
19 }
20 
21 int main()
22 {
23     int n;
24     scanf("%d",&n);
25     for(int i=1;i<=n;i++) scanf("%d",&d[i]);
26     if(n==1)
27     {
28         if(d[1]==0) printf("1\n");
29         else printf("0\n");
30     }
31     else
32     {
33         int sum=0;
34         for(int i=1;i<=n;i++)
35         {
36             if(d[i]==0||d[i]>=n) {printf("0\n");return 0;}
37             sum+=(--d[i]);
38         }
39         if(sum!=n-2) printf("0\n");
40         else
41         {
42             for(int i=1;i<=n;i++) cnt[i]=0; 
43             for(int i=2;i<=n-2;i++) cal(i,1);
44             for(int i=1;i<=n;i++) for(int j=2;j<=d[i];j++) cal(j,-1);
45             LL ans=1;
46             for(int i=1;i<=n;i++) while(cnt[i]--) ans=1LL*ans*i;
47             printf("%lld\n",ans);
48         }
49     }
50     return 0;
51 }
View Code
相關文章
相關標籤/搜索