BZOJ 1005 prufer序列
阿新 • • 發佈:2018-12-01
給出標號為1到N的點,以及某些點最終的度數,允許在任意兩點間連線,可產生多少棵度數滿足要求的樹?
第一行為N(0 < N < = 1000),接下來N行,第i+1行給出第i個節點的度數Di,如果對度數不要求,則輸入-1
Prufer數列是無根樹的一種數列。
在組合數學中,Prufer數列由有一個對於頂點標過號的樹轉化來的數列,點數為n的樹轉化來的Prufer數列長度為n-2。
樹轉Prufer:
- 找到編號最小的度數為11的點
- 刪除該節點並在序列中新增與該節點相連的節點的編號
- 重複1,21,2操作,直到整棵樹只剩下兩個節點
Prufer轉樹:
- 每次取出prufer序列中最前面的元素uu
- 在點集中找到編號最小的沒有在prufer序列中出現的元素vv
- 給u,vu,v連邊然後分別刪除
- 最後在點集中剩下兩個節點,給它們連邊
性質:
#include <bits/stdc++.h> using namespace std; int d[1005]; struct bigint { int a[7000], len; bigint() { memset(a, 0, 28000), len = 1View Code; } bigint operator* (const int &rhs) const { bigint ans; ans.len = len + 6; for(int i = 1; i <= len; ++i) ans.a[i] += a[i] * rhs; for(int i = 1; i < ans.len; ++i) if(ans.a[i] > 9) { ans.a[i+ 1] += ans.a[i] / 10; ans.a[i] %= 10; } while(!ans.a[--ans.len]); return ans; } bigint operator/ (const int &rhs) const { bigint ans; ans = *this, ++ans.len; for(int i = ans.len; i; --i) { ans.a[i - 1] += ans.a[i] % rhs * 10; ans.a[i] /= rhs; } while(!ans.a[--ans.len]); return ans; } }; int main() { int n, sum = 0, cnt = 0; bigint ans; scanf("%d", &n); for(int i = 1; i <= n; ++i) { scanf("%d", d + i); if(!d[i]) { puts("0"); return 0; } if(~d[i]) ++cnt, sum += d[i] - 1; } if(sum > 2 * n - 2) { puts("0"); return 0; } ans.a[1] = 1; for(int i = n - 1 - sum; i < n - 1; ++i) ans = ans * i; for(int i = 1; i <= n - 2 - sum; ++i) ans = ans * (n - cnt); for(int i = 1; i <= n; ++i) for(int j = 2; j <= d[i] - 1; ++j) ans = ans / j; for(int i = ans.len; i; --i) printf("%d", ans.a[i]); puts(""); return 0; }