【WC2019】数树

Statement

有$n$个节点, 分别用红线,蓝线连成两棵树. 用$y$种颜色给节点染色, 规定如果一条边在两棵树中同时出现, 那么边两端的点的颜色必须相同.

  • Task #1: 给定两棵树, 求染色方案.
  • Task #2: 给定其中一棵树, 求对于另一棵树的每一种形态的染色方案数之和.
  • Task #3: 两棵树的形态都没有确定, 求对于所有情况的染色方案数之和.

($n\le 10^5$)

Solution

Task #1

计算有多少条公共边即可.

Task #2

如果有 $i$ 条公共边, 那么贡献为 $y^{n-i}$ . 设 $z=y^{-1}$ , 贡献为 $y^n z^{i}$ , 考虑计算 $z^i$ ,最后乘上 $y^n$ .

于是我们对于边集 $E$ 的每个子集 $E’$ 的贡献为 $(z-1)^{|E’|}$ .

对于一个公共边的边集的选取方案, 将边连起来, 形成了很多个连通块, 大小为 $a_1\dots a_m$ , 那么贡献为:

$(z-1)^{n-m}$ 为贡献, $n^{m-2}\prod_{i=1}^m a_i$ 为 $n$ 个点, $m$ 个块的树的个数.

上面这个式子的组合意义为在每个块中选一个点的方案数, 树形DP即可, 时间复杂度 $O(n)$ .

Task #3

类似Task2的思路, 枚举至少有 $n-m$ 条公共边, 即 $m$ 个连通块的方案数:

设 $f_k$ 表示当前已经处理了 $k$ 个块, 那么有转移:

写成卷积的形式:

分治FFT即可, 时间复杂度 $O(n\log^2n)$ .

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
namespace Subtask0 {
map<LL, int> mp;
void Main() {
For(i, 1, n - 1) {
int x = read<int>(), y = read<int>();
mp[(LL)x * 1e9 + y] = 1;
}
int ans = 0;
For(i, 1, n - 1) {
int x = read<int>(), y = read<int>();
if (mp[(LL)x * 1e9 + y]) ++ ans;
}
printf("%d\n", fpm(y, n - ans));
}
}

namespace Subtask1 {
int beg[MAXN], v[MAXN << 1], nex[MAXN << 1], e = 1;
void add(int uu, int vv) {
v[++ e] = vv, nex[e] = beg[uu], beg[uu] = e;
}
int dp[MAXN][2];
void DFS(int u, int pa) {
dp[u][0] = (LL)fpm(n, MOD - 2) * fpm(z - 1, n - 1) % MOD;
for (int i = beg[u]; i; i = nex[i])
if (v[i] != pa) {
DFS(v[i], u);
int t0 = dp[u][0], t1 = dp[u][1];
dp[u][0] = (LL)t0 * dp[v[i]][1] % MOD * n % MOD * n % MOD * fpm(fpm(z - 1), n) % MOD;
dp[u][1] = (LL)t1 * dp[v[i]][1] % MOD * n % MOD * n % MOD * fpm(fpm(z - 1), n) % MOD;
(dp[u][0] += (LL)t0 * dp[v[i]][0] % MOD * n % MOD * fpm(fpm(z - 1), n - 1) % MOD) %= MOD;
(dp[u][1] += (LL)t0 * dp[v[i]][1] % MOD * n % MOD * fpm(fpm(z - 1), n - 1) % MOD) %= MOD;
(dp[u][1] += (LL)t1 * dp[v[i]][0] % MOD * n % MOD * fpm(fpm(z - 1), n - 1) % MOD) %= MOD;
}
(dp[u][1] += dp[u][0]) %= MOD;
}
void Main() {
if (y == 1) {
printf("%d\n", fpm(n, n - 2));
return;
}
For(i, 2, n) {
int u = read<int>(), v = read<int>();
add(u, v), add(v, u);
}
DFS(1, 0);
printf("%lld\n", (LL)fpm(y, n) * dp[1][1] % MOD);
}
}

namespace Subtask2 {
int f[MAXN << 1];
void cdqFFT(int l, int r) {
static int A[MAXN << 1], B[MAXN << 1];
if (l == r) {
if (!l) f[0] = (LL)fpm(fpm(n), 4) * fpm(z_, n) % MOD;
else f[l] = (LL)f[l] * iz_ % MOD * fac[l - 1] % MOD;
return;
}
int mid = (l + r) >> 1, len = 1, pt = 0;
cdqFFT(l, mid);
while (len < mid - l + 1) ++ pt, len <<= 1;
Rep(i, len) {
A[i] = (LL)f[l + i] * ifac[l + i] % MOD;
if (i) B[i] = (LL)fpm(i, i) * ifac[i - 1] % MOD * n % MOD * n % MOD;
}
For(i, len, len * 2 - 1) {
A[i] = 0;
B[i] = (LL)fpm(i, i) * ifac[i - 1] % MOD * n % MOD * n % MOD;
}
calcrev(len << 1, pt + 1);
NTT(A, len << 1, 1), NTT(B, len << 1, 1);
Rep(i, len << 1) A[i] = (LL)A[i] * B[i] % MOD;
NTT(A, len << 1, -1);
For(i, mid + 1, r) inc(f[i], A[i - l]);
cdqFFT(mid + 1, r);
}
void Main() {
if (y == 1) {
printf("%d\n", fpm(n, 2 * n - 4));
return;
}

InitFac(n);
z_ = z - 1, iz_ = fpm(z_);

cdqFFT(0, N - 1);

printf("%lld\n", (LL)f[n] * fpm(y, n) % MOD);
}
}