【prufer编码】BZOJ1211 [HNOI2004]树的计数

Description

  给定一棵树每个节点度的限制为di,求有多少符合限制不同的树。

 

Solution

  发现prufer码和度数必然的联系

  prufer码一个点出现次数为它的度数-1

  我们依然可以把树转成序列进行处理

  只是每个元素出现次数受到了限制

  于是就是有重复元素的排列问题了

  公式很好推

 

Code

  特殊情况判一判

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #define ll long long
 5 using namespace std;
 6 const int maxn=155;
 7 
 8 int dy[maxn],pri[maxn];
 9 int tot[maxn],cnt;
10 int d[maxn],n,sum;
11 
12 int getpri(){
13     for(int i=2;i<=n;i++){
14         if(!dy[i]) pri[++cnt]=i,dy[i]=cnt;
15         for(int j=1;j<=cnt&&i*pri[j]<=n;j++){
16             dy[pri[j]*i]=j;
17             if(i%pri[j]==0) break;
18         }
19     }
20 }
21 
22 int add(int x,int k){
23     while(x!=1){
24         tot[dy[x]]+=k;
25         x/=pri[dy[x]];
26     }
27 }
28 
29 ll pow(ll x,ll k){
30     ll ret=1;
31     for(int i=k;i;i>>=1,x=x*x)
32         if(i&1) ret=ret*x;
33     return ret;
34 }
35 
36 int main(){
37     scanf("%d",&n);
38     for(int i=1;i<=n;i++)
39         scanf("%d",&d[i]),sum+=d[i];
40     if(sum!=2*n-2){
41         printf("0\n");
42         return 0;
43     }
44     if(n==1){
45         printf("1\n");
46         return 0;
47     }
48     
49     getpri();
50     
51     for(int i=1;i<=n-2;i++) add(i,1);
52     for(int i=1;i<=n;i++)
53         if(!d[i]){
54             printf("0\n");
55             return 0;
56         }
57         else for(int j=1;j<d[i];j++) add(j,-1);
58     
59     ll ans=1;
60     for(int i=1;i<=cnt;i++)
61         ans=ans*pow(1ll*pri[i],tot[i]);
62     printf("%lld\n",ans);
63     return 0;
64 }

 

posted @ 2015-06-22 19:27  CyanNode  阅读(240)  评论(0编辑  收藏  举报