abcdeffa's Blog

当局者迷,旁观者清。

0%

GMOJ S3805 【小 X 的二叉堆计数】

Description

给你一个正整数 $n$ ,请你求出 $n$ 个互不相同的数能构成多少个不同的大小为 $n$ 的二叉堆,答案模 $(10 ^ 9 + 7)$ 。

Solution

考虑 递推 ,设 $f[i]$ 表示大小为 $i$ 的二叉堆的个数,那么设 $i$ 号点的左儿子为 $l$ 号点,右儿子为 $r$ 号点,并设 $size_i$ 表示以 $i$ 号点为根的子树的大小。那么有 $f[i] = (_{size_l} ^ {\;i - 1})f[size_l]f[size_r]$ 。

当只有一个儿子时, $f[i] = f[size_l]$ 。

对于组合数,我们可以先把阶乘预处理出来,当然也可以顺便预处理逆元,其实逆元暴力算也行。

到这里,我们得到了一个时间复杂度为 $O(n \log (10^9 +7))$ 的做法。

题解说会卡,然而我过了,而且跑得不慢。

此题还有 $O(n)$ 的做法,交由读者练习。

Code

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
#include <cstdio>
#define mod 1000000007LL
#define maxN 5000010
long long size[maxN], fac[maxN], f[maxN];
long long n = 0;
long long pow (long long x, long long y)
{
if(!y)
{
return 1;
}
else
{
long long dq = pow(x, y / 2);
if(!(y % 2))
{
return dq * dq % mod;
}
else
{
return dq * dq % mod * x % mod;
}
}
}
long long inv (long long x)
{
return pow(x, mod - 2);
}
long long C (long long n, long long m)
{
return fac[n] * inv(fac[m]) % mod * inv(fac[n - m]) % mod;
}
void dfs (long long x)
{
size[x] = 1;
long long lson = x * 2, rson = x * 2 + 1;
if(lson <= n)
{
dfs(lson);
size[x] += size[lson];
if(rson <= n)
{
dfs(rson);
size[x] += size[rson];
if(!f[size[x]])
{
f[size[x]] = C(size[x] - 1, size[lson]) * f[size[lson]] % mod * f[size[rson]] % mod;
}
}
else
{
if(!f[size[x]])
{
f[size[x]] = f[size[lson]] % mod;
}
}
}
}
int main ()
{
scanf("%lld", &n);
fac[0] = 1;
for(long long i = 1;i <= n; i++)
{
fac[i] = fac[i - 1] * i % mod;
}
if(n == 1)
{
printf("1");
return 0;
}
f[1] = 1;
dfs(1);
printf("%lld", f[n] * 2 % mod);
return 0;
}