1005: [HNOI2008]明明的烦恼

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 6539  Solved: 2558
[Submit][Status][Discuss]

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

首先要知道 Prufer数列 和 节点度数 这种东西,自行Google。

Plufer数列有一下性质:

1、可以表示为任意一个长度为n-2的数列

2、任意一点的度数为d[i],则该节点在数列中出现d[i]-1次

3、因此数列的总长度为:

\[sum=\sum _{i=1}^{n} (d[i]-1)\]

得出这个总长度的前提是,所有度数已知,但题目并没有给出所有的度数。但是我们可以计算出已知n个点的度数,可以构造出多少种Prufer数列:

\[\frac{(n-2)!}{\prod _{i-1}^{n}(d[i]-1)!}\]

那么已知cnt个点的度数,可以构造出多少种Prufer数列呢?

\[C_{n-2}^{sum}\times \frac{sum!}{\prod _{i-1}^{n}(d[i]-1)!}\]

那么已知cnt个点的度数,n-cnt个点的度数未知,可以构造出多少种Prufer数列呢?

\[ans=C_{n-2}^{sum}\times\frac{sum!}{\prod _{i-1}^{n}(d[i]-1)!}\times(n-cnt)^{n-2-sum}\]

最终可以化简得:

\[ans=\frac{(n-2)!}{(n-2-sum)!\prod _{i-1}^{n}(d[i]-1)!}\times(n-cnt)^{n-2-sum}\]

 

沿用了之前高精度乘除单精度的模板,减少代码量(明明是增加了)

虽说质因数分解更能显示bigger。。。

 

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<iomanip>
  6 using namespace std;
  7 
  8 const int MAXN=1000;
  9 const int DLEN=4;
 10 const int WIDE=10000;
 11 class BigNum
 12 {
 13 public:
 14     int NUM[MAXN];
 15     int L;
 16     bool flag;
 17     BigNum(){memset(NUM,0,sizeof(NUM));L=1;flag=0;}
 18     BigNum(const BigNum &T){memcpy(NUM,T.NUM,sizeof(NUM));L=T.L;flag=T.flag;}
 19     BigNum(int n){memset(NUM,0,sizeof(NUM));NUM[0]=n;L=1;while(NUM[L-1]>=WIDE){NUM[L]+=NUM[L-1]/WIDE;NUM[L-1]%=WIDE;L++;}flag=0;}
 20 };
 21 
 22 void Output(const BigNum T)
 23 {
 24     if(T.flag==1) cout<<'-';
 25     cout<<T.NUM[T.L-1];
 26     for(int i=T.L-2;i>=0;i--)
 27     {
 28         cout.width(DLEN);
 29         cout.fill('0');
 30         cout<<T.NUM[i];
 31     }
 32 }
 33 
 34 BigNum Mult(const BigNum A,int B)
 35 {
 36     BigNum C(A);
 37     int i,tmp,k=0;
 38     for(i=0;i<C.L||k;i++)
 39     {
 40         tmp=C.NUM[i]*B+k;
 41         k=tmp/WIDE;
 42         C.NUM[i]=tmp%WIDE;
 43     }
 44     C.L=i;
 45     return C;
 46 }
 47 
 48 BigNum Div(const BigNum A,int B)
 49 {
 50     BigNum C(A);
 51     int k=0;
 52     for(int i=C.L-1;i>=0;i--)
 53     {
 54         k=k*WIDE+C.NUM[i];
 55         C.NUM[i]=k/B;
 56         k%=B;
 57     }
 58     while(C.NUM[C.L-1]==0) C.L--;
 59     return C;
 60 }
 61 
 62 int n,sum,cnt;
 63 int d[1005];
 64 bool flag;
 65 BigNum ans(1);
 66 
 67 int main()
 68 {
 69     scanf("%d",&n);
 70     for(int i=1;i<=n;i++)
 71     {
 72         scanf("%d",&d[i]);
 73         if(d[i]==0||d[i]>n-1) flag=1;
 74         if(d[i]==-1) continue;
 75         sum+=d[i]-1;
 76         cnt++;
 77     }
 78     if(n==1)
 79     {
 80         if(d[1]==0||d[1]==-1) printf("1\n");
 81         else printf("0\n");
 82         return 0;
 83     }
 84     if(n==2)
 85     {
 86         if((d[1]==0||d[1]>1)&&(d[2]==0||d[2]>1)) printf("1\n");
 87         else printf("0\n");
 88         return 0;
 89     }
 90     if(flag)
 91     {
 92         printf("0\n");
 93         return 0;
 94     }
 95     for(int i=n-1-sum;i<=n-2;i++)
 96         ans=Mult(ans,i);
 97     for(int i=1;i<=n-2-sum;i++)
 98         ans=Mult(ans,n-cnt);
 99     for(int i=1;i<=cnt;i++)
100         for(int j=1;j<=d[i]-1;j++)
101             ans=Div(ans,j);
102     Output(ans);
103     return 0;
104 }

 

posted @ 2018-07-10 21:26  InWILL  阅读(211)  评论(0编辑  收藏  举报