【普呂弗編碼 + 組合數學】bzoj 1211: [HNOI2004]樹的計數
阿新 • • 發佈:2018-11-15
1211: [HNOI2004]樹的計數
Description
一個有n個結點的樹,設它的結點分別為v1, v2, …, vn,已知第i個結點vi的度數為di,問滿足這樣的條件的不同的樹有多少棵。給定n,d1, d2, …, dn,程式設計需要輸出滿足d(vi)=di的樹的個數。
Input
第一行是一個正整數n,表示樹有n個結點。第二行有n個數,第i個數表示di,即樹的第i個結點的度數。其中1<=n<=150,輸入資料保證滿足條件的樹不超過10^17個。
Output
輸出滿足條件的樹有多少棵。
Sample Input
4
2 1 2 1
Sample Output
2
思路
根據普呂弗編碼(關於Pruper Sequence
https://blog.csdn.net/baiyifeifei/article/details/84065899 ),我們已知,任何一棵樹,都唯一對應一段長度為n-2的序列,序列中每個點出現的次數都等於其在樹中的度數-1,因此假設有n個點,我們可以得到答案
值得注意的是當只有一個節點時,需要特判,假如這個點度數不為0則無法構成樹,而點大於1時,不能有點的度數為0,且必須出現的數的次數之和必須等於n-2
AC程式碼
/**************************************************************
Problem: 1211
User: FlyWhite
Language: C++
Result: Accepted
Time:0 ms
Memory:1292 kb
****************************************************************/
#include<cstdio>
#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
using namespace std;
typedef long long LL;
const int size=160;
int du[size];
int tim[size];
bool prime[size];
int p[size],tot;
void init_prime(int n)
{
tot=0;
fill(prime,prime+size,true);
for(int i=2;i<=n;i++)
{
if(prime[i] ) p[tot++]=i;
for(int j=0;j<tot&&i*p[j]<n;j++)
{
prime[i*p[j]]=false;
if(i%p[j]==0) break;
}
}
}
void solve(int num,int op)
{
for(int i=2;i<=num;i++)
{
int w=i;
for(int j=0;j<tot;j++)
{
if(w%p[j]==0)
while(w%p[j]==0)
{
w/=p[j];
tim[j]+=op;
}
}
}
}
int main()
{
int n;
scanf("%d",&n);
int sum=0;
memset(tim,0,sizeof(tim));
init_prime(n);
for(int i=1;i<=n;i++)
{
scanf("%d",&du[i]);
if(n==1) continue;
if(!du[i])
{
printf("0\n");
return 0;
}
du[i]--;
sum+=du[i];
}
if(n==1) {
if(!du[1]) printf("1\n");
else printf("0\n");
return 0;
}
if(sum!=n-2) {
printf("0");
return 0;
}
solve(n-2,1);
long long ans=1;
for(int i=1;i<=n;i++)
{
if(du[i]>1)solve(du[i],-1);
}
for(int i=0;i<tot;i++)
{
for(int j=1;j<=tim[i];j++)
{
ans*=p[i];
}
}
printf("%lld",ans);
}