題目的難度顏色使用 Luogu 上的分級,由簡單到困難分別為 🔴🟠🟡🟢🔵🟣⚫。

🔗 CF2178F Conquer or of Forest

rating: 2232

Problem Statement

題目簡述

給定一棵以節點 11 為根、nn 個節點的樹。根據子樹大小的奇偶性對節點著色:偶數為白色,奇數為黑色。

操作:選擇一個非根的白色節點 ww,斷開 (w,parent(w))(w, \text{parent}(w)) 邊,在保持樹的結構的前提下,在任意兩點間加一條新邊,然後根據子樹大小的奇偶性重新著色。

目標:使所有白色節點都位於從根到某節點的路徑上(或不存在白色節點)。

求透過任意次操作能構造的不同「目標樹」數量,答案對 998244353998244353 取模。

Constraints

約束條件

  • 每棵樹的節點數 nn2n2×1052 \le n \le 2 \times 10^5
  • 保證輸入是一棵樹

思路:計數

關鍵觀察:斷開白點形成連通分量

白點的性質

白色節點的子樹大小為偶數。因此,如果我們斷開所有白色節點 ww 與其父節點 pwp_w 之間的邊,整棵樹會被切分成若干個連通分量。

設總共形成 k+1k + 1 個連通分量:S0,S1,S2,,SkS_0, S_1, S_2, \ldots, S_k,其中:

  • S0S_0 是包含根節點 11 的連通分量
  • S1,S2,,SkS_1, S_2, \ldots, S_k 是其餘 kk 個連通分量

連通分量的固定性質

固定性質

無論執行多少次操作,對於 S0S_0 以外的連通分量而言,這些連通分量的大小始終保持不變,且每個連通分量內恰好有一個白色節點,該白點是連通分量中距離根最近的節點。

為什麼連通分量的大小不會改變?

  1. 黑色節點的父邊不能被移除:操作只能選擇白色節點來斷邊,因此黑色節點與其父節點之間的邊永遠不會被切斷。
  2. 連通分量內部結構固定:由於連通分量內除了根(白點)以外都是黑點,而黑點的父邊不可移除,所以連通分量內部的邊結構是不變的。
  3. 顏色會自動調整:每個連通分量的大小為偶數(因為白點的子樹大小為偶數)。當連通分量被重新連接到樹中時,距離根最近的那個節點會自動變成白色。

目標條件的轉化

目標

要讓所有白色節點都在從根到某一點的簡單路徑上,實際上我們需要把所有連通分量串成一條單鏈

S0Sp1Sp2SpkS_0 \to S_{p_1} \to S_{p_2} \to \cdots \to S_{p_k}

這條鏈不能有分叉,否則就會有兩個白色節點不在同一條從根出發的路徑上。

連接方案數的計算

當我們要把連通分量 SBS_B 接在連通分量 SAS_A 下面時:

  1. SAS_A 中選一個節點 uu(有 SA|S_A| 種選擇)。
  2. SBS_B 中選一個節點 vv(有 SB|S_B| 種選擇)。
  3. 連邊 (u,v)(u, v)。此時 vv 成為 SBS_B 連通分量在當前樹中的新根,而 SBS_B 整體大小為偶數,所以 vv 變白。

因此,將連通分量 SBS_B 接到 SAS_A 上的方案數是 SA×SB|S_A| \times |S_B|

推導總方案數

假設我們選定的排列順序是 S0Sp1Sp2SpkS_0 \to S_{p_1} \to S_{p_2} \to \cdots \to S_{p_k},連接方案數為:

S0×Sp12×Sp22××Spk12×Spk|S_0| \times |S_{p_1}|^2 \times |S_{p_2}|^2 \times \cdots \times |S_{p_{k-1}}|^2 \times |S_{p_k}|

又因為 (i=1kSpi)=(i=1kSi)\left(\prod_{i=1}^{k} |S_{p_i}|\right) = \left(\prod_{i=1}^{k} |S_i|\right),我們可以把式子改寫成:

S0×(i=1kSi2)×1Spk|S_0| \times \left(\prod_{i=1}^{k} |S_i|^2\right) \times \frac{1}{|S_{p_k}|}

我們需要對 S1,,SkS_1, \ldots, S_k 的所有 k!k! 種排列求和。考慮哪個連通分量在鏈尾:假設 SjS_j 在鏈尾,那麼剩下的 k1k-1 個連通分量有 (k1)!(k-1)! 種排列方式。因此總答案為:

j=1k[(k1)!×S0×(i=1kSi2)×1Sj]\sum_{j=1}^{k} \left[(k-1)! \times |S_0| \times \left(\prod_{i=1}^{k} |S_i|^2\right) \times \frac{1}{|S_j|}\right]

提出與 jj 無關的因子後:

S0×(k1)!×(i=1kSi2)×(j=1k1Sj)|S_0| \times (k-1)! \times \left(\prod_{i=1}^{k} |S_i|^2\right) \times \left(\sum_{j=1}^{k} \frac{1}{|S_j|}\right)

最終公式

ans=S0×(k1)!×(i=1kSi2)×(j=1kSj1)(mod998244353)\text{ans} = |S_0| \times (k-1)! \times \left(\prod_{i=1}^{k} |S_i|^2\right) \times \left(\sum_{j=1}^{k} |S_j|^{-1}\right) \pmod{998244353}

特殊情況

k=0k = 0 的情況

k=0k = 0 時,只有連通分量 S0S_0,表示沒有除了根節點之外的白色節點。此時樹已滿足目標條件,且不存在可以操作的連通分量,答案為 11

複雜度分析

  • 時間複雜度:O(n)\mathcal{O}(n)
  • 空間複雜度:O(n)\mathcal{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
from functools import reduce

MOD = 998244353

MAX_N = int(2e5 + 5)
fact = [1] * (MAX_N + 1)
for i in range(1, MAX_N + 1):
fact[i] = (fact[i - 1] * i) % MOD

def solve():
n = int(input())
g = [[] for _ in range(n)]
for _ in range(n - 1):
u, v = map(lambda x: int(x) - 1, input().split())
g[u].append(v)
g[v].append(u)

comps = []
sz = [0] * n
def dfs(u, fa):
# sz[u] = 1
# for v in g[u]:
# if v == fa:
# continue
# dfs(v, u)
# if sz[v] & 1:
# sz[u] += sz[v]
# else:
# comps.append(sz[v])
st = [(u, fa, 0)]
while st:
u, fa, i = st.pop()
if i == 0:
sz[u] = 1
if i > 0:
v = g[u][i - 1]
if sz[v] & 1:
sz[u] += sz[v]
else:
comps.append(sz[v])
for j in range(i, len(g[u])):
v = g[u][j]
if v == fa:
continue
st.append((u, fa, j + 1))
st.append((v, u, 0))
break
dfs(0, -1)

if len(comps) == 0:
print(1)
return

ans = (sz[0] * fact[len(comps) - 1]) % MOD
ans = (ans * reduce(lambda x, y: (x * y * y) % MOD, comps, 1)) % MOD
ans = (ans * reduce(lambda x, y: (x + y) % MOD, map(lambda x: pow(x, MOD - 2, MOD), comps), 0)) % MOD
print(ans)

if __name__ == "__main__":
t = int(input())
for _ in range(t):
solve()

寫在最後

PROMPT

這裡什麼都沒有。

Paper Tiger.