1. 程式人生 > >【樹】【數論】[BZOJ1005][HNOI2008]明明的煩惱

【樹】【數論】[BZOJ1005][HNOI2008]明明的煩惱

題目描述

自從明明學了樹的結構,就對奇怪的樹產生了興趣…… 給出標號為1到N的點,以及某些點最終的度數,允許在任意兩點間連線,可產生多少棵度數滿足要求的樹?

樣例輸入

3
1
-1
-1

樣例輸出

2

題目解析

首先我們根據prufer數列可以知道任意一棵無根樹可以表示為任意一個長度為n2的串並且有以下的性質任意一點的度為di那麼該數字將會在數列中出現di1次,那麼我們可以知道該數列的總長度就是sum=ni=1di1當然前提是n個度數全部已知,那麼我們已經知道了n個點的度數,我們可以構造出多少不同的prufer數列呢可以發現答案就是

(n2)!ni=1
(di1)!
但是我們現在並不知道這麼多,我們現在已知的有cnt個,那麼我們未知的有ncnt個,那麼我們如果不管不知道的,但是現在有n2個空位所以答案是Csumn2sum!ni=1(di1)!但是現在我們還有(ncnt)個未知那麼我們的答案就是Csumn2sum!ni=1(di1)!×(ncnt)n2sum那麼我們化簡可以得到(n2)!sum!(n2sum)!sum!ni=1(di1)!×(ncnt)n2sum(n2)!(n2sum)!ni=1(di1)!×(ncnt)n2sum因為n2還是比較大所以靠分解質因數來解決高精度的問題。

關於prufer唯一性其實很好證明如果兩個prufer的數列是一樣的那麼意味著每一個節點的每一個兒子數量相同,並且出入度和位置相同,那難道不一樣麼。

程式碼

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 1000;
int answ[MAXN+10], prime[MAXN+10], t[MAXN*10+10], d[MAXN+10];
bool notprime[MAXN+10];
void GetPrime(int Max){
    int tmp;
    for(int i=2;i<=Max;i++){
        if(!notprime[i])
            prime[++prime[0
]] = i; for(int j=1;j<=prime[0]&&(tmp=prime[j]*i)<=Max;j++){ notprime[tmp] = true; if(i%prime[j] == 0) break; } } } void add(int u, int m){ for(int i=1;i<=prime[0]&&u>1;i++){ while(u%prime[i]==0){ u /= prime[i]; answ[i] += m; } } } int main(){ int n; scanf("%d", &n); int sum=0, cnt=0, flag = 0; GetPrime(1000); int t1; for(int i=1;i<=n;i++){ scanf("%d", &t1); d[i] = t1; if(t1 == 0 || t1 >= n) flag = 1; if(t1 == -1){ continue; }else{ cnt++; sum += (--t1); for(int j=1;j<=t1;j++) add(j, -1); } } t1 = n-2; for(int i=1;i<=t1;i++) add(i, 1); if(n == 1){ if(t1 == -1) printf("1\n"); else printf("0\n"); return 0; } if(n == 2){ if((d[1]==0||d[1]>1) || (d[2]==0||d[2]>1)) printf("0\n"); else printf("1\n"); return 0; } if(flag){ printf("0\n"); return 0; } t1 = n-2-sum; for(int i=1;i<=t1;i++) add(i, -1); add(n-cnt, n-2-sum); t[0] = t[1] = 1; for(int i=1;i<=prime[0];i++){ while(answ[i]){ answ[i]--; for(int j=1;j<=t[0];j++) t[j] *= prime[i]; for(int j=1;j<t[0];j++){ t[j+1] += t[j] / 10; t[j] %= 10; } while(t[t[0]] >= 10){ t[t[0]+1] = t[t[0]]/10; t[t[0]] %= 10; t[0]++; } } } for(int i=t[0];i;i--) printf("%d", t[i]); printf("\n"); return 0; }