您的位置:首页 > Web前端

bzoj1005: [HNOI2008]明明的烦恼 prufer编码

2018-03-04 15:59 225 查看

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−1di−1,于是全排列然后再分别排列即可得出以下式子

(di−1)!" role="presentation" style="text-align: center; position: relative;">ans=(n−2)!∏(di−1)!ans=(n−2)!∏(di−1)!

然而只有部分是确定的。所以我们先考虑确定的一部分,假设有cnt个,那么它们在prufer序列中占位子一共有

sum=∏1cntdi−1sum=∏1cntdi−1

我们在长度为n-2的prufer序列中挑sum个位子给这些确定的数,在乘以这些确定的数本身的方案数就是这些数的答案,即

Csumn−2⋅sum!∏1cnt(di−1)!Cn−2sum⋅sum!∏1cnt(di−1)!

剩下的空位有n-sum-2个,每一个位置共有n-cnt种选择,产生的贡献是。

(n−cnt)n−sum−2(n−cnt)n−sum−2

所以总的答案就是

ans=Csumn−2⋅sum!∏1cnt(di−1)!⋅(n−cnt)n−sum−2ans=Cn−2sum⋅sum!∏1cnt(di−1)!⋅(n−cnt)n−sum−2

=(n−2)!⋅(n−cnt)n−sum−2∏1cnt(di−1)!⋅(n−sum−2)!=(n−2)!⋅(n−cnt)n−sum−2∏1cnt(di−1)!⋅(n−sum−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
, mn
, a
, t
;
bool vis
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: