題目大意
自從明明學了樹的結構,就對奇怪的樹產生了興趣......
給出標號為 1 到 N 的點,以及某些點最終的度數,允許在任意兩點間連線,可產生多少棵度數滿足要求的樹?
Input
第一行為 N(0<N<=1000),接下來 N 行,第 i+1 行給出第 i 個節點的度數 Di,如果對度數不要求,則輸入 -1
Output
一個整數,表示不同的滿足要求的樹的個數,無解輸出 0
做法分析
這題需要了解一種數列: Purfer Sequence
我們知道,一棵樹可以用括號序列來表示,但是,一棵頂點標號(1~n)的樹,還可以用一個叫做 Purfer Sequence 的數列表示
一個含有 n 個節點的 Purfer Sequence 有 n-2 個數,Purfer Sequence 中的每個數是 1~n 中的一個數
一個定理:一個 Purfer Sequence 和一棵樹一一對應
先看看怎么由一個樹得到 Purfer Sequence
由一棵樹得到它的 Purfer Sequence 總共需要 n-2 步,每一步都在當前的樹中尋找具有最小標號的葉子節點(度為 1),將與其相連的點的標號設為 Purfer Sequence 的第 i 個元素,並將此葉子節點從樹中刪除,直到最后得到一個長度為 n-2 的 Purfer Sequence 和一個只有兩個節點的樹
看看下面的例子:
假設有一顆樹有 5 個節點,四條邊依次為:(1, 2), (1, 3), (2, 4), (2, 5),如下圖所示:
第 1 步,選取具有最小標號的葉子節點 3,將與它相連的點 1 作為第 1 個 Purfer Number,並從樹中刪掉節點 3:
第 2 步,選取最小標號的葉子節點 1,將與其相連的點 2 作為第 2 個 Purfer Number,並從樹中刪掉點 1:
第 3 步,選取最小標號的葉子節點 4,將與其相連的點 2 作為第 3 個 Purfer Number,並從樹中刪掉點 4:
最后,我們得到的 Purfer Sequence 為:1 2 2
不難看出,上面的步驟得到的 Purfer Sequence 具有唯一性,也就是說,一個樹,只能得到一個唯一的 Purfer Sequence
接下來看,怎么由一個 Purfer Sequence 得到一個樹
由 Purfer Sequence 得到一棵樹,先將所有編號為 1 到 n 的點的度賦初值為 1,然后加上它在 Purfer Sequence 中出現的次數,得到每個點的度
先執行 n-2 步,每一步,選取具有最小標號的度為 1 的點 u 與 Purfer Sequence 中的第 i 個數 v 表示的頂點相連,得到樹中的一條邊,並將 u 和 v 的度減一
最后再把剩下的兩個度為 1 的點連邊,加入到樹中
我們可以根據上面的例子得到的 Purfer Sequence :1 2 2 重新得到一棵樹
Purfer Sequence 中共有 3 個數,可以知道,它表示的樹中共有 5 個點,按照上面的方法計算他們的度為下表所示:
頂點 | 1 | 2 | 3 | 4 | 5 |
度 | 2 | 3 | 1 | 1 | 1 |
第 1 次執行,選取最小標號度為 1 的點 3 和 Purfer Sequence 中的第 1 個數 1 連邊:
將 1 和 3 的度分別減一:
頂點 | 1 | 2 | 3 | 4 | 5 |
度 | 1 | 3 | 0 | 1 | 1 |
第 2 次執行,選取最小標號度為 1 的點 1 和 Purfer Sequence 中的第 2 個數 2 連邊:
將 1 和 2 的度分別減一:
頂點 | 1 | 2 | 3 | 4 | 5 |
度 | 0 | 2 | 0 | 1 | 1 |
第 3 次執行,將最小標號度為 1 的點 4 和 Purfer Sequence 第 3 個數 2 連邊:
將 2 和 4 的度分別減一:
頂點 | 1 | 2 | 3 | 4 | 5 |
度 | 0 | 1 | 0 | 0 | 1 |
最后,還剩下兩個點 2 和 5 的度為 1,連邊:
至此,一個 Purfer Sequence 得到的樹畫出來了,由上面的步驟可知,Purfer Sequence 和一個樹唯一對應
綜上,一個 Purfer Sequence 和一棵樹一一對應
有了 Purfer Sequence 的知識,這題怎么搞定呢?
先不考慮無解的情況,從 Purfer Sequence 構造樹的過程中可知,一個點的度數減一表示它在 Purfer Sequence 中出現了幾次,那么:
假設度數有限制的點的數量為 cnt,他們的度數分別為:d[i]
另:
那么,在 Purfer Sequence 中的不同排列的總數為:
而剩下的 n-2-sum 個位置,可以隨意的排列剩余的 n-cnt 個點,於是,總的方案數就應該是:
化簡之后為:
在有解的情況下,計算該結果輸出就行了
無解的情況非常好確定,這里就再討論了
參考代碼

1 import java.util.*; 2 import java.math.*; 3 4 public class Main { 5 static int n, d[]=new int[10002]; 6 static BigInteger p[]=new BigInteger[1002]; 7 static BigInteger ans; 8 9 static public void main(String args[]) { 10 Scanner IN=new Scanner(System.in); 11 n=IN.nextInt(); 12 int sum=0, flag=0, cnt=0; 13 for(int i=0; i<n; i++) { 14 d[i]=IN.nextInt(); 15 if(d[i]==0 || d[i]>n-1) flag=1; 16 if(d[i]==-1) continue; 17 sum+=d[i]-1; 18 cnt++; 19 } 20 IN.close(); 21 if(n==1) { 22 if(d[0]==0 || d[0]==-1) System.out.println(1); 23 else System.out.println(0); 24 return; 25 } 26 if(n==2) { 27 if((d[0]==-1 || d[0]==1) && (d[1]==-1 || d[1]==-1)) System.out.println(1); 28 else System.out.println(0); 29 return; 30 } 31 if(flag==1) System.out.println(0); 32 p[0]=BigInteger.ONE; 33 for(int i=1; i<=n; i++) p[i]=p[i-1].multiply(BigInteger.valueOf(i)); 34 ans=p[n-2].divide(p[n-2-sum]); 35 for(int i=0; i<n; i++) { 36 if(d[i]==-1) continue; 37 ans=ans.divide(p[d[i]-1]); 38 } 39 for(int i=0; i<n-2-sum; i++) ans=ans.multiply(BigInteger.valueOf(n-cnt)); 40 System.out.println(ans); 41 } 42 }
題目鏈接 & AC 通道