自從明明學了樹的結構,就對奇怪的樹產生了興趣......php
給出標號爲 1 到 N 的點,以及某些點最終的度數,容許在任意兩點間連線,可產生多少棵度數知足要求的樹?java
Inputide
第一行爲 N(0<N<=1000),接下來 N 行,第 i+1 行給出第 i 個節點的度數 Di,若是對度數不要求,則輸入 -1spa
Output3d
一個整數,表示不一樣的知足要求的樹的個數,無解輸出 0code
這題須要瞭解一種數列: Purfer Sequenceblog
咱們知道,一棵樹能夠用括號序列來表示,可是,一棵頂點標號(1~n)的樹,還能夠用一個叫作 Purfer Sequence 的數列表示ip
一個含有 n 個節點的 Purfer Sequence 有 n-2 個數,Purfer Sequence 中的每一個數是 1~n 中的一個數get
一個定理:一個 Purfer Sequence 和一棵樹一一對應event
先看看怎麼由一個樹獲得 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 }