考え方
連結成分ごとの答えをかけ合わせれば良い.「どの頂点についても、その頂点から他の頂点に向かう辺がちょうど 1 本ずつ存在する」とき,頂点と辺は1セットと考えることができ
- (頂点数) = (辺数)
逆に,「(頂点数) = (辺数)」であれば「どの頂点についても、その頂点から他の頂点に向かう辺がちょうど 1 本ずつ存在する」.具体的には,
- ループの外の辺は,ループに向かうように向きづけし,
- ループの向きは,2通りのうち片方を選べばよい
以上より,各連結成分の向きの付け方は,ループの向きだけとなる.
まとめると,各連結成分の向き付けの方法を
- 「(頂点数) = (辺数)」なら向き付けの方法は2通り
- 「(頂点数) ≠ (辺数)」なら向き付けの方法は0通り
回答例
import sys sys.setrecursionlimit(10 ** 6) N, M = map(int, input().split()) mod = 998244353 p = [-1] * (N + 1) def root(x): if p[x] < 0: return x p[x] = root(p[x]) return p[x] def unite(x, y): x = root(x) y = root(y) if x == y: return p[x] += p[y] p[y] = x def size(x): x = root(x) return -p[x] edge = [0] * (N + 1) for _ in range(M): u, v = map(int, input().split()) if root(u) != root(v): edge[root(u)] += edge[root(v)] + 1 else: edge[root(u)] += 1 unite(u, v) ans = 1 for v in range(1, N + 1): if v != root(v): continue if size(v) == edge[v]: ans *= 2 ans %= mod else: ans = 0 print(ans)
ここでは,edge
で各連結成分の辺の数を,edge[根]
で管理した.
他の計算方法として,次の回答では,
- 各頂点から出ている辺の本数
degree
を計算し, - 全部計算が終わったところで,連結成分に属する頂点から出ている辺の本数の合計値
degree_cnt[根]
を計算し, - 連結成分の辺の数を
degree_cnt[根] // 2
で求める,
【競プロ実況】ABC226 E問題【かつっぱ】 - YouTube (Submission #27093388 - AtCoder Beginner Contest 226)
参考
アライグマ「E問題は、どの辺も「どれかの頂点から出てる」から、「どの頂点からも1本しか辺が出てない」ってことはそもそも(辺数)=(頂点数)じゃない連結成分があったら答えは0なのだ! (辺数)=(頂点数)の連結グラフはかならずサイクルが1個あって、サイクル以外のところは端から自動的に決まるから」 pic.twitter.com/0kfQEZVtVR
— 競技プログラミングをするフレンズ (@kyopro_friends) November 7, 2021
アライグマ「結局、サイクルのところをどっち向きにするか2通りしか決め方はないのだ。ということは2^(サイクルの個数)=2^(連結成分の個数)が答えなのだ!」
— 競技プログラミングをするフレンズ (@kyopro_friends) November 7, 2021