BZOJ 1211 树的计数

http://www.lydsy.com/JudgeOnline/problem.php?id=1211

思路:每一个prufer编码都代表了一棵树,而点的度数,代表了它在prufer编码中出现的次数+1,因此,我们就知道每个点在prufer编码中出现的次数,用组合数就可以解决:

最后的式子约一下是: cnt!/(∏(du[i]-1)!)

要分解质因数,不然会爆longlong

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
ll s[50];
int p[20005],n,du[200005],num[20005];
int read(){
    int t=0,f=1;char ch=getchar();
    while (ch<'0'||'9'<ch){if (ch=='-') f=-1;ch=getchar();}
    while ('0'<=ch&&ch<='9'){t=t*10+ch-'0';ch=getchar();}
    return t*f;
}
bool jud(int x){
    for (int i=2;i<=sqrt(x);i++)
     if (x%i==0) return 0;
    return 1; 
}
void init(){
    for (int i=2;i<=150;i++)
     if (jud(i)) p[++p[0]]=i;
}
void solve(ll x,int v){
    if (x==0) return;
    for (int i=1;i<=p[0];i++){
        if (x<=1) return;
        while (x%p[i]==0){
            num[i]+=v;
            x/=p[i];
        }
    }
}
int main(){
    n=read();
    s[1]=1;
    s[0]=1;
    for (int i=2;i<=22;i++) s[i]=s[i-1]*i;
    init();
    ll ans=1,tot=0;
    for (int i=1;i<=n;i++){
     du[i]=read(),du[i]--,tot+=du[i];
    }
    if (n==1){
        if (tot==-1) puts("1");
        else puts("0");
        return 0;
    } 
    for (int i=1;i<=n;i++){
        if (du[i]<0) {
            puts("0");
            return 0;
        }
    }
    if (tot!=n-2){
        puts("0");
        return 0;
    } 
    solve(s[n-2],1);
    for (int i=1;i<=n;i++)
     solve(s[du[i]],-1);
    for (int i=1;i<=p[0];i++)
     for (int j=1;j<=num[i];j++)
      ans*=p[i];
    printf("%lld\n",ans);   
    return 0;
}

 

posted @ 2016-07-16 15:24  GFY  阅读(215)  评论(0编辑  收藏  举报