【bzoj1005】[HNOI2008]明明的煩惱 Prufer序列+高精度
阿新 • • 發佈:2017-12-28
合數 ++ sizeof 數學 return prufer 情況 noi2008 con
題目描述
給出標號為1到N的點,以及某些點最終的度數,允許在任意兩點間連線,可產生多少棵度數滿足要求的樹?
輸入
第一行為N(0 < N < = 1000),接下來N行,第i+1行給出第i個節點的度數Di,如果對度數不要求,則輸入-1
輸出
一個整數,表示不同的滿足要求的樹的個數,無解輸出0
樣例輸入
3
1
-1
-1
樣例輸出
2
題解
Prufer序列+高精度
Prufer序列:由一棵 $n$ 個點的樹唯一產生的一個 $n-2$ 個數的序列。
生成方法:找到這棵樹編號最小的葉子節點,將其相鄰點加入到序列中,刪掉這個點。重復這個過程直到樹中只剩下兩個點,此時得到的序列即為該樹的Prufer序列。
性質:任何一個長度為 $n-2$ ,每個數均在 $1\sum n$ 之間的序列均為一個合法的Prufer序列,對應且只對應著一棵 $n$ 個點的樹。
性質:在原樹中度數為 $d$ 的點,在Prufer序列中出現了 $d-1$ 次。
根據這兩個性質可以考慮本題。給出了每個點的度數限制,即給出了每個點在Prufer序列中出現的次數。對於沒給限制的,可以隨意選擇。
相當於先在 $n-2$ 個數中選出一部分作為沒有限制的,剩下的是有限制的。
對於沒有限制的,答案就是 $沒限制的位置個數^沒限制的點的個數$ 。
對於有限制的,使用組合數學的一個公式:長度為 $\sum a_i$ 的序列,第 $i$ 個數出現了 $a_i$ 次的序列數為 $\frac{(\sum a_i)!}{\prod(a_i!)}$ 。
本題不取模,為避免高精度除法,將階乘分解質因數來處理。
註意特判無解的情況。
#include <cstdio> #include <cstring> #include <algorithm> #define mod 100000000 using namespace std; typedef long long ll; struct data { int len; ll v[400]; ll &operator[](int a) {return v[a];} data operator+(data &a) { data ans; memset(ans.v , 0 , sizeof(ans.v)); int i; for(i = 0 ; i < len || i < a.len || ans[i] ; i ++ ) ans[i] += v[i] + a[i] , ans[i + 1] = ans[i] / mod , ans[i] %= mod; ans.len = i; return ans; } data operator*(int a) { data ans; memset(ans.v , 0 , sizeof(ans.v)); int i; for(i = 0 ; i < len || ans[i] ; i ++ ) ans[i] += v[i] * a , ans[i + 1] = ans[i] / mod , ans[i] %= mod; ans.len = i; return ans; } void write() { int i; printf("%lld" , v[len - 1]); for(i = len - 2 ; ~i ; i -- ) printf("%08lld" , v[i]); puts(""); } }ans; int a[1010] , cnt[1010] , prime[1010] , tot , np[1010]; void init() { int i , j; for(i = 2 ; i <= 1000 ; i ++ ) { if(!np[i]) prime[++tot] = i; for(j = 1 ; j <= tot && i * prime[j] <= 1000 ; j ++ ) { np[i * prime[j]] = 1; if(i % prime[j] == 0) break; } } } void solve(int x , int a) { int i , j; for(i = 1 ; i <= tot ; i ++ ) for(j = x / prime[i] ; j ; j /= prime[i]) cnt[i] += a * j; } int main() { init(); int n , i , c1 = 0 , c2 = 0; scanf("%d" , &n); for(i = 1 ; i <= n ; i ++ ) { scanf("%d" , &a[i]); if(a[i] > 0) c1 += a[i] - 1; else if(a[i] == -1) c2 ++ ; else { puts("0"); return 0; } } if(c1 > n - 2) { puts("0"); return 0; } solve(n - 2 , 1) , solve(n - 2 - c1 , -1); for(i = 1 ; i <= n ; i ++ ) if(a[i] > 0) solve(a[i] - 1 , -1); ans[0] = ans.len = 1; for(i = 1 ; i <= tot ; i ++ ) while(cnt[i] -- ) ans = ans * prime[i]; for(i = 1 ; i <= n - 2 - c1 ; i ++ ) ans = ans * c2; ans.write(); return 0; }
【bzoj1005】[HNOI2008]明明的煩惱 Prufer序列+高精度