yukicoder No.629 グラフの中に眠る門松列
2018/02/24 表現を修正
writerしました
問題
- https://yukicoder.me/problems/no/629
- ざっくり説明:グラフと頂点に割り当てる数字が与えられる.門松パスは存在するか?
本記事の目的
- グラフに含まれる,門松パスの個数を O(M) で求める.
- cielさんの提出コード https://yukicoder.me/submissions/228617 を参考にしました.
表記について
- deg(v) は,頂点vの次数を表す.
基本
- グラフ内に長さ2のパスはいくつ存在するか?
- 頂点
v
を中心とする長さ2のパスを数え上げれば良い. - 時間計算量は O(|E|) . ( 頂点次数の数え上げに O(deg(v)) 掛かると考えた場合.通常は O(1) なので O(|V|)で済む. )
- 頂点
- 余談: N頂点から成るクリークなグラフ(完全グラフ)に含まれる,長さ2のパスの個数は N*(N-1)*(N-2)/2 個
応用
グラフ内に🎍パスはいくつ存在するか?
- 頂点 v を中心とする長さ2の🎍パスを数え上げれば良い.
- 隣接頂点に書かれている数字を洗い出して ( 時間計算量O(deg(v)) ),えいっ( 時間計算量O(deg(v)) )とやれば,時間計算量 O(|E|) で全て求まりそう.
えいっとやる方法を考えるため,補題を考える.
えいっ
自然数 a_0 と N 要素から成る自然数のmultiset a_1 , ..., a_N が与えれる. a_1 , ... , a_N から2つ選び,a_0 を中心とした🎍列を作りたい. 何通り作ることが出来るか?ただし,左右反転した列は同一の列とみなす.
a_0が中央であることが分かっているので,a_0の左右には a_0 より小さい数が2つ,または a_0 より大きな数が2つ必ず来る必要がある.
- 中央が小さい門松列,中央が大きい門松列と別に数え上げることが出来る.
a_0の左右に a_0 より小さい数が2つ来るケースを考えてみる.
- a_0 より小さい数 を抽出した multiset b_1 , ..., b_M ⊆{a_1...a_N} を用意する.
- 全てのペアの個数は, M*(M-1)/2 .
- 🎍列でないパターンは,ペアの両端が同じ数の場合.このペアの個数を引いていく.
- 辞書や配列で管理して,同じ数字が何個現れるか求めて,そのペアの個数を求める.
a_0 より大きい数も同様.
以上で時間計算量O(N)でこの補題の答えが求まった.
- 本題に戻る.
- 別の問題に含まれる変数に,『中心の頂点vに書かれた数字をa_0,頂点vに隣接する頂点に書かれた数字をa_1, ... 』と割り当てて解くと,頂点 v を中心とする長さ2の🎍パスの個数が求まる.
- 頂点vに隣接する頂点の個数はO(deg(v))
- 全ての頂点に対してこの計算を行うので,元の問題の全体の時間計算量はO(|E|)
コード
def ascan; gets.split.map(&:to_i);end N, M = ascan A = ascan # 隣接リスト adjacents = Array.new(N){[]} M.times do u, v = ascan u -= 1; v -= 1 adjacents[u] << v adjacents[v] << u end ans = 0 # 頂点vを中心とする長さ2のパスについて調べたい 0.upto(N-1) do |v| # 頂点 v に書かれた数字 a = A[v] # aより小さな数字が書かれた頂点 {key:数字, value:個数} small = Hash.new(0) # aより大きな数字が書かれた頂点 large = Hash.new(0) adjacents[v].each do |u| small[A[u]] += 1 if A[u] < a large[A[u]] += 1 if A[u] > a end # aが最大となる🎍パスの数え上げ unless small.empty? cnt = small.values.reduce(:+) ans += cnt*(cnt-1)/2 small.each do |key, val| ans -= val*(val-1)/2 end end # aが最小となる🎍パスの数え上げ unless large.empty? cnt = large.values.reduce(:+) ans += cnt*(cnt-1)/2 large.each do |key, val| ans -= val*(val-1)/2 end end end # 個数 # p ans puts ans > 0 ? :YES: :NO
感想
この解法でも☆2.5くらいですね…