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编码的题,关于prufer编码的学习,推荐一篇文章:prufer编码学习
然后开始推公式
首先我们考虑所有点的度数都确定的情况。
prufer序列中每个数字出现的次数是
di−1
d
i
−
1
,于是全排列然后再分别排列即可得出以下式子
ans=(n−2)!∏(di−1)!
a
n
s
=
(
n
−
2
)
!
∏
(
d
i
−
1
)
!
然而只有部分是确定的。所以我们先考虑确定的一部分,假设有cnt个,那么它们在prufer序列中占位子一共有
sum=∏1cntdi−1
s
u
m
=
∏
1
c
n
t
d
i
−
1
我们在长度为n-2的prufer序列中挑sum个位子给这些确定的数,在乘以这些确定的数本身的方案数就是这些数的答案,即
Csumn−2⋅sum!∏1cnt(di−1)!
C
n
−
2
s
u
m
⋅
s
u
m
!
∏
1
c
n
t
(
d
i
−
1
)
!
剩下的空位有n-sum-2个,每一个位置共有n-cnt种选择,产生的贡献是。
(n−cnt)n−sum−2
(
n
−
c
n
t
)
n
−
s
u
m
−
2
所以总的答案就是
ans=Csumn−2⋅sum!∏1cnt(di−1)!⋅(n−cnt)n−sum−2
a
n
s
=
C
n
−
2
s
u
m
⋅
s
u
m
!
∏
1
c
n
t
(
d
i
−
1
)
!
⋅
(
n
−
c
n
t
)
n
−
s
u
m
−
2
=(n−2)!⋅(n−cnt)n−sum−2∏1cnt(di−1)!⋅(n−sum−2)!
=
(
n
−
2
)
!
⋅
(
n
−
c
n
t
)
n
−
s
u
m
−
2
∏
1
c
n
t
(
d
i
−
1
)
!
⋅
(
n
−
s
u
m
−
2
)
!
这个东西会爆longlong,质因数分解然后高精度一下即可。
/**************************************************************
Problem: 1005
User: 2014lvzelong
Language: C++
Result: Accepted
Time:52 ms
Memory:1312 kb
****************************************************************/
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int read() {
char ch = getchar(); int x = 0, f = 1;
for(;ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
for(;ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) - '0' + ch;
return x * f;
}
const int N = 1100;
int n, tot, cnt, sum, pri[N], mn[N], a[N], t[N];
bool vis[N];
void GP(int N) {
for(int i = 2;i <= N; ++i) {
if(!mn[i]) pri[++tot] = i, mn[i] = tot;
for(int j = 1;j <= tot && i * pri[j] <= N; ++j) {
mn[i * pri[j]] = j;
if(!(i % pri[j])) break;
}
}
}
void add(int b, int f) {
for(int j = mn[b]; b != 1; j = mn[b])
for(;!(b % pri[j]); b /= pri[j]) a[j] += f;
}
void mul(int x) {
for(int i = 1;i <= t[0]; ++i)
t[i] *= x;
for(int j = 1;j < t[0]; ++j) {
t[j + 1] += t[j] / 10;
t[j] %= 10;
}
for(;t[t[0]] >= 10; ++t[0]) {
t[t[0] + 1] = t[t[0]] / 10;
t[t[0]] %= 10;
}
}
int main() {
n = read(); GP(1000); t[0] = t[1] = 1;
if(n == 1) {
int x = read();
if(!x) puts("1");
else puts("0");
return 0;
}
for(int i = 1;i <= n; ++i) {
int t = read();
if(!t) return puts("0") * 0;
if(~t) {
++cnt; sum += --t;
for(int i = 1;i <= t; ++i)
add(i, -1);
}
}
if(sum > n - 2) return puts("0") * 0;
for(int i = n - 1 - sum; i <= n - 2; ++i) add(i, 1);
for(int i = 1;i <= tot; ++i)
while(a[i]--) mul(pri[i]);
for(int i = 1;i <= n - 2 - sum; ++i) mul(n - cnt);
for(int i = t[0]; i; --i) printf("%d", t[i]);
puts("");
return 0;
}