cychester

BZOJ 1005[HNOI2008]明明的烦恼

 

Description

 

  自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在
任意两点间连线,可产生多少棵度数满足要求的树?

Input

  第一行为N(0 < N < = 1000),
接下来N行,第i+1行给出第i个节点的度数Di,如果对度数不要求,则输入-1

Output

  一个整数,表示不同的满足要求的树的个数,无解输出0

 

Solution

首先我们知道了prufer数列 传送门.

也知道prufer数列中某个数字出现的次数 = 节点的度数 - 1

现题目要求某些节点的读书固定, 就是让它在数列中出现的次数固定。

先不考虑不存在树的情况(特判就懒得写了

定义$d_i$为节点$i$的度数, $sum = \sum\limits_{i=1}^n{d_i-1}$ , $t$ 为被要求的节点的个数

则数列中固定的数的位置方案有 $C^{sum}_{n-1} \times sum! / \prod\limits_{i=1}^n{(d_i-1)!}$种,

剩下 来$n-t$个节点与剩下来$n-sum-2$个位置, 有$(n-t)^{n-2-sum}$种方案

两部分不影响, 乘起来就可以得出答案。

数据有点大, 需要打高精乘法。

Code

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define rd read()
 5 #define ll long long
 6 using namespace std;
 7 
 8 const int N = 2e3;
 9 const ll base = 1e8;
10 
11 int n, sum, c[N], x;
12 int pri[N], tot, vis[N], cnt[N];
13 
14 struct BIG {
15     ll s[N];
16     BIG() {
17         s[0] = 0;
18         memset(s, 0,sizeof(s));
19     }
20     BIG operator * (const ll &t) const{
21         BIG re;
22         re.s[0] = s[0];
23         ll x = 0;
24         for(int i = 1; i <= s[0]; ++i) {
25             re.s[i] = s[i] * t;
26             x = re.s[i] / base;
27             re.s[i] %= base;
28         }
29         if(x) re.s[++re.s[0]] = x;
30         return re;
31     }
32 }ans;
33 
34 int read() {
35     int X = 0, p = 1; char c = getchar();
36     for(; c > '9' || c < '0'; c  = getchar()) if(c == '-') p = -1;
37     for(; c >= '0' && c <= '9'; c = getchar()) X = X * 10 + c - '0';
38     return X * p;
39 }
40 
41 ll fpow(ll a, ll p) {
42     if(!a) return 1;
43     ll re = 1;
44     for(; p; p >>= 1, a = a * a) if(p & 1) re = re * a;
45     return re;
46 }
47 
48 void init() {
49     for(int i = 2; i < N; ++i) {
50         if(!vis[i]) pri[++tot] = i;
51         for(int j = 1;  j <= tot && pri[j] * i < N; ++j) {
52             vis[i * pri[j]] = 1;
53             if(i % pri[j] == 0) break;
54         }
55     }
56 }
57 
58 void cal(int x, int k) {
59     for(int i = 1; x != 1 && i <= tot; ++i) if(x % pri[i] == 0) {
60         while(x % pri[i] == 0) cnt[i] += k, x /= pri[i];
61     }
62 }
63 
64 void print(BIG t) {
65     printf("%lld", t.s[t.s[0]]);
66     for(int i = t.s[0] - 1; i; --i) printf("%08lld", t.s[i]);
67     putchar('\n');
68 }
69 
70 int main()
71 {
72     n = rd;
73     init();
74     ans.s[0] = 1;
75     ans.s[1] = 1;
76     for(int i = 1; i <= n; ++i) {
77         c[i] = rd;
78         if(c[i] > 0 && n == 1) return printf("0\n"), 0;
79         if(c[i] != -1) {
80             if(c[i] == 0 && n != 1) return printf("0\n"), 0;
81           c[i]--;    x++;
82             sum += c[i];
83             for(int j = 2; j <= c[i]; ++j) cal(j, -1);
84         }
85     }
86     if(n == 1) return printf("1\n"), 0;
87     if(sum > n - 2) return printf("0\n"), 0;
88     if(x == n && sum != n - 2) return printf("0\n"), 0;
89 
90     for(int i = 1; i <= n - 2; ++i) cal(i, 1);
91     for(int i = 1; i <= n - sum - 2; ++i) cal(i, -1);
92 
93     for(int i = 1; i <= n - sum - 2; ++i) ans = ans * (n - x);
94     
95     for(int i = 1; i <= tot; ++i) 
96         for(int j = 1; j <= cnt[i]; ++j) ans = ans * pri[i];
97     print(ans);
98 }
View Code

 

posted on 2018-08-21 15:27  cychester  阅读(170)  评论(0编辑  收藏  举报

导航