BZOJ1005: [HNOI2008]明明的烦恼

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编码的性质:
  (1)树的prufer编码的实现
        不断 删除树中度数为1的最小序号的点,并输出与其相连的节点的序号  直至树中只有两个节点
  (2)通过观察我们可以发现
        任意一棵n节点的树都可唯一的用长度为n-2的prufer编码表示
        度数为m的节点的序号在prufer编码中出现的次数为m-1
  (3)怎样将prufer编码还原为一棵树??
        从prufer编码的最前端开始扫描节点,设该节点序号为 u ,寻找不在prufer编码的最小序号且没有被标记的节点 v ,连接   u,v,并标记v,将u从prufer编码中删除。扫描下一节点。
 
 
 
 
该题需要将树转化为prufer编码:
 n为树的节点数,d[ ]为各节点的度数,m为无限制度数的节点数。
则            
所以要求在n-2大小的数组中插入tot各序号,共有种插法;
在tot各序号排列中,插第一个节点的方法有种插法;
                           插第二个节点的方法有种插法;
                                      ………
另外还有m各节点无度数限制,所以它们可任意排列在剩余的n-2-tot的空间中,排列方法总数为
 
根据乘法原理:
 
 
然后就要高精度了…..但高精度除法太麻烦了,显而易见的排列组合一定是整数,所以可以进行质因数分解,再做一下相加减。
 
 
关于n!质因数分解有两种方法,第一种暴力分解,这里着重讲第二种。
  若p为质数,则n!可分解为 一个数*,其中  <n
 
所以 

——转自怡红公子

链接:http://www.cnblogs.com/noip/archive/2013/03/10/2952520.html

 

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <cstring>
 4 struct bignum {
 5     int l, a[10000];
 6      
 7     bignum (int t) {
 8         l = 0; memset(a, 0, sizeof(0));
 9         while (t != 0) {
10             a[++l] = t % 10000;
11             t /= 10000;
12         }
13         if (l == 0) l = 1;
14     }
15      
16     void mul(int k) {
17         for (int i = 1; i <= l; i++)
18             a[i] *= k;
19         for (int i = 1; i <= l; i++)
20             if (a[i] >= 10000) {
21                 int t = i;
22                 do {
23                     a[t + 1] += a[t] / 10000;
24                     a[t++] %= 10000;
25                 } while (a[t] >= 10000);
26             }
27         while (a[l + 1] > 0) l++;
28     }
29      
30     void print() {
31         printf("%d", a[l]);
32         for (int i = l - 1; i >= 1; i--)
33             printf("%04d", a[i]);
34         printf("\n");
35     }
36 };
37 int n, m = 0, tot = 0, d[1001], prime[1001], cnt[1001];
38  
39 bool judge(int k) {
40     for (int i = 2; i <= sqrt(k); i++)
41         if (k % i == 0) return false;
42     return true;
43 }
44  
45 void makelist(int n) {
46     prime[0] = 0;
47     for (int i = 2; i <= n; i++)
48         if (judge(i)) prime[++prime[0]] = i;
49 }
50  
51 void compute(int k, int t) {
52     for (int i = 1; i <= prime[0] && prime[i] <= k; i++) {
53         int x = 0, n = k, p = prime[i];
54         while (n != 0) {
55             x += n / p;
56             n /= p;
57         }
58         cnt[i] += x * t;
59     }
60 }
61  
62 int main() {
63     //freopen("input.txt", "r", stdin);
64     //freopen("output.txt", "w", stdout);
65     scanf("%d", &n);
66     for (int i = 1; i <= n; i++) {
67         scanf("%d", &d[i]);
68         if (d[i] == -1) m++;
69             else tot += d[i] - 1;
70     }
71      
72     makelist(n);
73     memset(cnt, 0, sizeof(cnt));
74     compute(n - 2, 1);
75     compute(n - 2 - tot, -1);
76     for (int i = 1; i <= n; i++)
77         if (d[i] != -1) compute(d[i] - 1, -1);
78  
79     bignum ans = 1;
80     for (int i = 1; i <= prime[0]; i++)
81         for (int j = 1; j <= cnt[i]; j++)
82             ans.mul(prime[i]);
83     for (int i = 1; i <= n - 2 - tot; i++)
84         ans.mul(m);
85     ans.print();
86     return 0;
87 }
BZOJ1005

 

posted @ 2015-11-02 19:06  减舟  阅读(235)  评论(0编辑  收藏  举报