1. 程式人生 > >BZOJ 1005 prufer序列

BZOJ 1005 prufer序列

給出標號為1到N的點,以及某些點最終的度數,允許在任意兩點間連線,可產生多少棵度數滿足要求的樹?

第一行為N(0 < N < = 1000),接下來N行,第i+1行給出第i個節點的度數Di,如果對度數不要求,則輸入-1

Prufer數列是無根樹的一種數列。

在組合數學中,Prufer數列由有一個對於頂點標過號的樹轉化來的數列,點數為n的樹轉化來的Prufer數列長度為n-2。

樹轉Prufer:

  1. 找到編號最小的度數為11的點
  2. 刪除該節點並在序列中新增與該節點相連的節點的編號
  3. 重複1,21,2操作,直到整棵樹只剩下兩個節點

  Prufer轉樹:

  1. 每次取出prufer序列中最前面的元素uu
  2. 在點集中找到編號最小的沒有在prufer序列中出現的元素vv
  3. u,vu,v連邊然後分別刪除
  4. 最後在點集中剩下兩個節點,給它們連邊

性質:

  

       

   

#include <bits/stdc++.h>
using namespace std;
int d[1005];
struct bigint
{
    int a[7000], len;

    bigint()
    {
        memset(a, 0, 28000), len = 1
; } 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; }
View Code