[HNOI2008][bzoj 1005]明明的烦恼(prufer序列)

1005: [HNOI2008]明明的烦恼

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 7121  Solved: 2816
[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序列来求解。

先来科普一下prufer的性质:

  1. 每个prufer序列都唯一对应着一棵树。
  2. prufer序列的长度等于它所对应的树的节点数-2。
  3. 每个数在prufer序列中出现的次数等于该节点在树中的度数-1。

其实有了这些性质我们就可以做题了(想要证明的自行度娘),现在我们在来观察一下这道题,如果他给出的是所有点的度数,那么这道题就是一个不全相异的全排列个数(戳这里),但是他给出的点的度数只是一部分的,那我们就可以先当别的点不存在,先把这一部分的方案数求出来,设$tot=\Sigma{d[i]-1}$,$tot$即为已经确定度数的点在prufer序列里所占的个数,这一部分方案数为$C_{n-2}^{tot}$,但是别忘了我们还要处理重复的部分,处理第一个数向$tot$个数中插的方案数为$C_{tot}^{d[1]-1}$,同理处理第二个数的方案数是$C_{tot-(d[1]-1)}^{d[2]-1}$,剩下的以此类推。

但是别忘了我们还有没确定度数的点,但是这很好处理,我们设未确定的点数为$cnt$,这就相当于在$n-2-tot$的空间中随便选$cnt$个,那么答案即为$cnt^{n-2-tot}$

然后我们根据乘法原理可以的出答案

$ans=C_{n-2}^{tot}*C_{tot}^{d[1]-1}*C_{tot-(d[1]-1)}^{d[2]-1}*\cdots*C_{d[i]-1}^{d[i]-1}*cnt^{n-2-tot}$

我们把组合数公式展开来一波化简就得到了结果(数学公式崩了,凑或者看吧qwq)

这样再用一个高精就阔以了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<queue>
 6 #include<vector>
 7 using namespace std;
 8 #define int long long
 9 const int N=500005;
10 int d[N];
11 struct BigInt{
12     int m[N];
13     friend void operator *= (BigInt &a,int b){
14         int x=0;
15         for(int i=1;i<=a.m[0];i++){
16             int y=a.m[i]*b+x;
17             a.m[i]=y%10;
18             x=y/10;
19         }
20         while(x){
21             a.m[++a.m[0]]=x%10;
22             x/=10;
23         }
24     }
25     friend void operator /= (BigInt &a,int b){
26         int x=0;
27         for(int i=a.m[0];i>=1;i--){
28             x+=a.m[i];
29             a.m[i]=x/b;
30             x%=b;
31             x*=10;
32         }
33         while(a.m[a.m[0]]==0&&a.m[0]>1) a.m[0]--;
34     }
35     friend void print(BigInt a){
36         for(int i=a.m[0];i>=1;i--) printf("%lld",a.m[i]);
37         puts("");
38     }
39 }x;
40 signed main(){
41     int n;
42     scanf("%lld",&n);
43     x.m[0]=x.m[1]=1;
44     int cnt=0,num=0;
45     if(n==1){
46         int k;
47         scanf("%lld",&k);
48         if(!k){puts("0");}
49         else puts("1");
50         return 0;
51     }
52     for(int i=1;i<=n;i++){
53         int mm;
54         scanf("%lld",&mm);
55         if(!mm){puts("0");return 0;}
56         if(mm==-1) cnt++;
57         else {d[i]=mm-1;num+=d[i];}
58     }
59     for(int i=1;i<=n-2;i++) x*=i;
60     //for(int i=1;i<=num;i++) x*=i;
61     //for(int i=2;i<=/*n-cnt-2*/num;i++) x*=i;
62     for(int i=1;i<=n-2-num;i++) x*=cnt,x/=i;
63     for(int i=1;i<=n;i++){
64         if(d[i]>0){
65             for(int j=1;j<=d[i];j++) x/=j; 
66         }
67     }
68     print(x);
69 }
View Code

 

posted @ 2019-07-24 07:37  Barça_10  阅读(246)  评论(0编辑  收藏  举报