BZOJ1005: [HNOI2008]明明的烦恼

1005: [HNOI2008]明明的烦恼

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 5471  Solved: 2131
[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

Source

 

【题解】

http://www.cnblogs.com/zhj5chengfeng/archive/2013/08/23/3278557.html 很赞的博客

用到的序列性质:

1、对于度数是i的点,这个点在prufer中出现的次数是i - 1.

2、n个节点的树,Prufer序列长度为n - 2

3、任意一个Prufer序列均对应于一颗树

 

我们假设有m个不确定的节点

从n-2个空位中选出sum = Σd[i]-1个空位,对sum进行有重复全排列,于是

C(sum, n-2) * sum!/((d[1]-1)! * (d[1]-2)! * ..)

剩下的n - 2 - sum个位置,m个节点全随机选:m^(n - 2 - sum)

于是

ans = C(sum, n-2) * sum! * m^(n - 2 - sum)/((d[1]-1)! * (d[1]-2)! * ..)

= (n - 2)! * m^(n - 2 - sum)/(d[1]!*d[2]!*...*d[n]!*(n - 2 - sum)!)

然后暴力分解,避免高精除法

我因为高精乘脑抽多加一个=调了一个小时。。

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cstdlib>
 5 
 6 inline void read(int &x)
 7 {
 8     x = 0;char ch = getchar(),c = ch;
 9     while(ch < '0' || ch > '9')c = ch, ch = getchar();
10     while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar();
11     if(c == '-')x = -x;
12 }
13 
14 const int MAXN = 1000 + 10;
15 
16 int n, d[MAXN], m;
17 int k[MAXN];
18 
19 int ans[100000], len, sum;
20 
21 void cheng(int a)
22 {
23     for(register int i = 1;i <= len;++ i)ans[i] *= a;
24     for(register int i = 1;i < len;++ i)ans[i + 1] += ans[i]/10, ans[i] %= 10;
25     for(;ans[len] >= 10;++ len)ans[len + 1] += ans[len]/10, ans[len] %= 10;
26 }
27 
28 /*
29 
30 C(n - 2 ,sum) * sum! * m^(n - 2 - sum)/(d[1]!*d[2]!*...*d[n]!) 
31 = (n - 2)! * m^(n - 2 - sum)/(d[1]!*d[2]!*...*d[n]!*(n - 2 - sum)!)
32 */
33 
34 int main()
35 {
36     read(n);
37     register int tmp = 0;
38     for(register int i = 1;i <= n;++ i)
39     {
40         read(d[i]);
41         if(d[i] == 0)
42         {
43             printf("0");
44             return 0;
45         }
46         if(d[i] == -1)
47         {
48             ++ m;
49             continue;
50         }
51         sum += d[i] - 1;
52         for(register int p = 2;p < d[i];++ p)
53         {
54             tmp = p;
55             for(register int j = 2;tmp > 1;++ j)
56                 while(tmp % j == 0) -- k[j],tmp/= j;
57         }
58     }
59     if(n == 1 && d[1] != 0)
60     {
61         printf("0");
62         return 0;
63     }
64     if(n == 2 && (d[1] != 1 || d[2] != 1))
65     {
66         printf("0");
67         return 0;
68     }
69     for(register int i = 2;i <= n - 2;++ i)
70     {
71         tmp = i;
72         for(register int j = 2;tmp > 1;++ j)
73             while(tmp % j == 0) ++k[j], tmp/= j;
74     }
75     for(register int i = 2;i <= n - 2 - sum;++ i)
76     {
77         tmp = i;
78         for(register int j = 2;tmp >1;++ j)
79             while(tmp % j == 0)-- k[j], tmp/= j;
80     }
81     tmp = m;
82     for(register int j = 2;tmp > 1;++ j)
83         while(tmp % j == 0)k[j] += n - 2 - sum, tmp/= j;
84         
85     ans[1] = 1, len = 1;
86     
87     for(int i = 1;i < MAXN;++ i)
88         for(;k[i] > 0;--k[i])cheng(i);
89     
90     for(register int i = len;i >= 1;-- i)printf("%d", ans[i]);
91     return 0;
92 } 
BZOJ1005

 

posted @ 2017-09-05 10:50  嘒彼小星  阅读(148)  评论(0编辑  收藏  举报