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.找到编号最小的叶节点,删除这个节点,然后与这个叶节点相连的点计入序列
2.反复进行1,直到这棵树只剩下两个节点时,退出
比如说这个图(来自度受百科)
最小叶节点为2,删除2,将3计入序列
最小叶节点为4,删除4,将5计入序列
最小叶节点为5,删除5,将1计入序列
最小叶节点为1,删除1,将3计入序列
图中只剩下两个节点,退出
于是得到这棵树的Prufer序列为{3,5,1,3}
这样可以得到一个长度为n-2的序列。很容易证明,树和Prufer序列是一一对应的
Prufer序列显然满足一个性质:一个点若度数为d,则一定在Prufer序列中出现了d-1次
于是这就变成了一个排列组合的问题了
令每个已知度数的节点的度数为di,有n个节点,m个节点未知度数,left=(n-2)-(d1-1)-(d2-1)-…-(dk-1)
已知度数的节点可能的组合方式如下
(n-2)!/(d1-1)!/(d2-1)!/…/(dk-1)!/left!
剩余left个位置由未知度数的节点随意填补,方案数为m^left
于是最后有
ans=(n-2)!/(d1-1)!/(d2-1)!/…/(dk-1)!/left! * m^left
这个地方可以直接暴力分解质因子来做。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int mod = 1000000;
int n, m, tot, cnt;
int d[1005], num[1005], pri[1005]; //num质因子分解
int ans[1005], l = 1;
bool isprime(int x){
for(int i = 2; i*i <= x; i++){
if(x%i == 0) return 0;
}
return 1;
}
void pre_init(){
for(int i = 2; i <= 1000; i++){
if(isprime(i)){
pri[++cnt] = i;
}
}
}
void fenjie(int a, int f){ //暴力分解a!的质因子
for(int k = 1; k <= a; k++){
int x = k;
for(int i = 1; i <= cnt; i++){
if(x <= 1) break;
while(x % pri[i] == 0){
num[i] += f;
x /= pri[i];
}
}
}
}
void chen(int x){
for(int i = 1; i <= l; i++){
ans[i] *= x;
}
for(int i = 1; i <= l; i++){ //压位计算
ans[i+1] += ans[i] / mod;
ans[i] %= mod;
}
while(ans[l+1] > 0){
l++;
ans[l+1] += ans[l]/mod;
ans[l] %= mod;
}
}
void output(){
for(int i = l; i > 0; i--){
if(i == l) printf("%d", ans[i]);
else printf("%06d", ans[i]);
}
}
int main(){
pre_init();
ans[1] = 1;
scanf("%d", &n);
if(n == 1){
int x;
scanf("%d", &x);
if(x == 0) puts("1");
else puts("0");
return 0;
}
for(int i = 1; i <= n; i++){
scanf("%d", &d[i]); //度数
if(d[i] == 0){
puts("0");
return 0;
}
if(d[i] == -1) m++; //自由度数的
else{
d[i]--;
tot += d[i];
}
}
if(tot > n - 2){ //prufer编码的性质不成立
puts("0");
return 0;
}
fenjie(n-2, 1); //分子
fenjie(n-2-tot, -1);//分母
for(int i = 1; i <= n; i++){
if(d[i]){
fenjie(d[i], -1);
}
}
for(int i = 1; i <= cnt; i++){
while(num[i]--){
chen(pri[i]);
}
}
for(int i = 1; i <= n - 2 - tot; i++){
chen(m);
}
output();
return 0;
}