【树】【数论】[BZOJ1005][HNOI2008]明明的烦恼

来源:互联网 发布:正在安装网络组件特曼 编辑:程序博客网 时间:2024/05/17 06:50

题目描述

自从明明学了树的结构,就对奇怪的树产生了兴趣…… 给出标号为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;}
1 0
原创粉丝点击