[bzoj1005][HNOI2008]明明的烦恼

来源:互联网 发布:连锁店管理系统源码 编辑:程序博客网 时间:2024/05/16 18:31

Description
  自从明明学了树的结构,就对奇怪的树产生了兴趣……给出标号为1到N的点,以及某些点最终的度数,允许在
任意两点间连线,可产生多少棵度数满足要求的树?
Input
  第一行为N(0 < N < = 1000),
接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1
Output
  一个整数,表示不同的满足要求的树的个数,无解输出0
Sample Input
3
1
-1
-1
Sample Output
2
HINT
  两棵树分别为1-2-3;1-3-2
分析:
1.树的Prufer编码的实现及其性质:
①每次都取出度数为1的节点,并输出与该节点相邻的点。重复此操作,直到这棵树只剩下两个点。
②任意一棵树都存在一个长度为n-2的Prufer序,度数为m的节点在Prufer序中出现m-1次
③n个节点度数分别为D1,D2,D3…Dn的树的个数为

(n2)!(D11)!(D21)!...(Dn1)!

(其实就是一个有重复元素的全排列)
2.本题需要用到Prufer序编码的相关知识
3.如果没有-1的点,那么答案就是性质③
4.把所有度数没有限制的点在一起考虑,设m为没有限制的节点个数,tot为限制的节点度数的和,则它们一共有
mn2tot
种不同的方案
5.最终的答案为
(n2)!mn2tot(n2tot)!(D11)!(D21)!...(Dn1)!
加上高精度就可以过了。

#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>using namespace std;const int maxn=10010;int a[maxn];int n;int sum;int tot[maxn];struct Bigint{    int a[maxn],len;    Bigint(){        len=1;        memset(a,0,sizeof(a));    }    Bigint operator *(int& x)const{        Bigint ret;        ret.len=len;        for(int i=1;i<=len;i++) ret.a[i]=a[i]*x;        for(int i=1;i<=len;i++) ret.a[i+1]+=ret.a[i]/10,ret.a[i]%=10;        while(ret.a[ret.len+1]) ret.len++,ret.a[ret.len+1]=ret.a[ret.len]/10,ret.a[ret.len]%=10;        return ret;    }}A;int cnt;int is_prime[maxn],prime[maxn];void getprime(){    for(int i=2;i<=1000;i++){        if(!is_prime[i]) prime[++cnt]=i;        for(int j=1;j<=cnt && i*prime[j]<=1000;j++){            is_prime[i*prime[j]]=1;            if(i%prime[j]==0) break;        }    }}void calc(int x,int c){    for(int i=2;i<=x;i++){        int p=i;        for(int j=1;j<=cnt;j++){            if(p<=1) break;            while(p%prime[j]==0){                p/=prime[j];tot[j]+=c;            }        }    }}int m;int main(){    scanf("%d",&n);    if(n==1){        int x;        scanf("%d",&x);        if(x)printf("0");        else printf("1");        return 0;    }    getprime();    for(int i=1;i<=n;i++){        scanf("%d",&a[i]);        if(a[i]>=n){            printf("0");            return 0;        }        if(a[i]!=-1) sum+=a[i]-1;        else m++;    }    if(sum>n-2){        printf("0");        return 0;    }    calc(n-2,1);    calc(n-2-sum,-1);    for(int i=1;i<=n;i++)if(a[i]!=-1){        calc(a[i]-1,-1);    }    A.a[1]=1;    for(int i=1;i<=cnt;i++)        for(int j=1;j<=tot[i];j++)            A=A*prime[i];    for(int i=1;i<=n-2-sum;i++) A=A*m;    for(int i=A.len;i>=1;i--) printf("%d",A.a[i]);    return 0;}

^_^

1 0
原创粉丝点击