明明的烦恼

###[BZOJ1005][HNOI2008] 明明的烦恼

给出一棵树中所有节点的度数( $-1$ 代表无限制),求可能的树的种类数。

$0<N\leq1000$

嘛,这是我少有的单独为一道题写的博客,这水题卡了我一晚上,不过不能否认,这道题对现在的我来说确实很有价值,本体需要先推公式,然后分解质因数搞高精。

我们考虑用 $pufer$ 编码来表示一棵树,根据 $pufer$ 编码的性质:

  • 任何一棵无根树都可以表示为长度为 $n-2$ 的串;
  • 一个点的度数是 $d$ ,则它会在串中出现 $d-1$ 次。

我们知道该已知度数的节点组成的串的总长度 $\displaystyle len=\sum_{i=1}^nd_i-1$ ,将长度为 $len$ 的串中的数插入到总的串中,方法有 $\binom{len}{n-2}$ 种,而长度为 $len$ 的串共有 $\displaystyle\binom{len}{d_1-1}\binom{len-(d_1-1)}{d_2-1}\dots\binom{d_n-1}{d_n-1}$ 种,再算上 $m$ 个无度数限制的节点的 $m^{n-2-len}$ 种,根据乘法原理:
$$
\begin{eqnarray}
ans&=&\binom{len}{n-2}\binom{len}{d_1-1}\binom{len-(d_1-1)}{d_2-1}\dots\binom{d_n-1}{d_n-1}m^{n-2-len}\
&=&\frac{(n-2)!}{(n-2-len)!len!}\cdot\frac{len!}{(d_1-1)!(len-d_1-1)!}\dots\frac{(d_n-1)!}{(d_n-1)!0!}\cdot m^{n-2-len}\
&=&\frac{(n-2)!\cdot m^{n-2-len}}{(n-2-len)!\prod_{i=1}^n(d_i-1)!}
\end{eqnarray}
$$

由于答案十分巨大,需要写高精,不过高精除比较恶心,所以我们考虑分解质因数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/**************************************************************
Problem: 1005
User: zhangche0526
Language: C++
Result: Accepted
Time:80 ms
Memory:1344 kb
****************************************************************/

#include<iostream>
#include<cstdio>

const int MAXN=1005;

int n;
int pIdx[MAXN],pri[MAXN],pcnt,dgr[MAXN];
bool notPri[MAXN];
void initPri(int r)
{
int i,j;
for(i=2;i<=r;i++)
{
if(!notPri[i]) pri[++pcnt]=i;
for(j=1;j<=pcnt&&pri[j]*i<=r;j++)
{
notPri[pri[j]*i]=true;
if(i%pri[j]==0) break;
}
}
}

void addIdx(int x,int idx)
{
for(int i=1;i<=pcnt&&x>1;i++)
while(x%pri[i]==0)
x/=pri[i],pIdx[i]+=idx;
}

int dig[MAXN*10],dL;
int main()
{
int i,j;
initPri(1000);
scanf("%d",&n);
if(n==1)
{
scanf("%d",&dgr[1]);
if(dgr[1]==0||dgr[1]==-1) printf("1\n");
else printf("0\n");
return 0;
}
if(n==2)
{
scanf("%d%d",&dgr[1],&dgr[2]);
if(dgr[1]==1&&dgr[2]==1) printf("1\n");
else printf("0\n");
return 0;
}
int dSum=0,limNcnt=0;bool noAns=false;
for(i=1;i<=n;i++)
{
scanf("%d",&dgr[i]);
if(dgr[i]==0||dgr[i]>=n) noAns=true;
if(dgr[i]==-1)
continue;
limNcnt++;
dSum+=dgr[i]-1;
for(j=1;j<=dgr[i]-1;j++)
addIdx(j,-1);
}
for(i=1;i<=n-2;i++) addIdx(i,1);
if(noAns){printf("0\n");return 0;}
for(i=1;i<=n-2-dSum;i++) addIdx(i,-1);
addIdx(n-limNcnt,n-2-dSum);
dL=dig[1]=1;
for(i=1;i<=pcnt;i++)
{
while(pIdx[i])
{
pIdx[i]--;
for(j=1;j<=dL;j++) dig[j]*=pri[i];
for(j=1;j<dL;j++) dig[j+1]+=dig[j]/10,dig[j]%=10;
while(dig[dL]>=10)
{
dig[dL+1]+=dig[dL]/10,dig[dL]%=10;
dL++;
}
}
}
for(i=dL;i;i--)
printf("%d",dig[i]);
printf("\n");
return 0;
}