NoiminのNoise

競技プログラミング (多め) とWeb (たまに) ,自然言語処理 (ブログではまだ)。数式の書き方を一気に KaTeX に変えようとして記事を全削除してインポートし直すなどしたので,過去にブックマークされた記事は URL が変わってしまっている可能性があります…….

Educational Codeforces Round 72 D. Coloring Edges

問題: https://codeforces.com/contest/1217/problem/D

公式解説: https://codeforces.com/blog/entry/69605

問題概要

$ n (\leq 5000) $ 頂点 $ m (\leq 5000) $ 辺の有向グラフがある.このグラフの辺を,「含まれる辺が全て同じ色で塗られた閉路」がないように彩色したい.色の数を最小化した上で条件を満たす彩色を1つ答えよ.

解法概要

グラフが閉路を含まない場合,自明に1彩色可能. グラフが閉路を含むかどうかの判定方法はいろいろあるが,私の解法ではトポロジカルソートで全頂点を並べることができれば閉路なし,できなければ閉路ありとしている.

グラフが閉路を含む場合,1彩色は不可能である. ここで,頂点番号に着目してみる. 頂点番号 $ u $ から頂点番号 $ v $ への辺があるとすると, 1つの閉路には $ u \gt v $ となるような辺と $ u \lt v $ となるような辺が必ず両方含まれる. これは例えば,$ u_1 \rightarrow u_2 \rightarrow \cdots \rightarrow u_l \rightarrow u_1 $ というような1つの閉路があるとき,以下の3つの場合に場合分けするとわかりやすい.

  • $ u_1 \rightarrow u_2 \rightarrow \cdots \rightarrow u_l $ の中に $ u_i \gt u_{i+1} $ となるような辺と $ u_i \lt u_{i+1} $ となるような辺が両方含まれていれば,閉路の中にも両方自明に含まれる.

  • $ u_1 \rightarrow u_2 \rightarrow \cdots \rightarrow u_l $ の中に $ u_i \gt u_{i+1} $ となるような辺のみなら,$ u_l \lt u_1 $ なので,閉路の中には両方含まれる.

  • $ u_1 \rightarrow u_2 \rightarrow \cdots \rightarrow u_l $ の中に $ u_i \lt u_{i+1} $ となるような辺のみなら,$ u_l \gt u_1 $ なので,閉路の中には両方含まれる.

したがって,$ u \gt v $ となるような辺を色1, $ u \lt v $ となるような辺を色2で彩色すれば問題の条件を満たす2彩色が可能である.また,色を3色以上使う必要はない.

ソースコード

#include <iostream>
#include <vector>
#include <set>
#include <algorithm>

using namespace std;

constexpr int MAX_N = 5000;

struct Edge {
    int u, v, color;
    Edge(int u, int v, int color): u(u), v(v), color(color) {}
};

int n, m, k = 1;
vector<int> G[MAX_N];
vector<Edge> edges;

vector<int> topological_sort(){
    int indeg[n];
    set<int> st; // 入次数が0の頂点の番号
    fill(indeg, indeg+n, 0);
    for(int i=0;i<n;++i) for(int to: G[i]) indeg[to]++;
    for(int i=0;i<n;++i) if(indeg[i] == 0) st.insert(i);

    vector<int> ret;
    int used[n];
    fill(used, used+n, 0);
    while(!st.empty()){
        int node = *st.begin();
        st.erase(node);
        ret.push_back(node);
        used[node] = 1;
        for(int next: G[node]){
            --indeg[next];
            if(!used[next] && indeg[next] == 0) st.insert(next);
        }
    }
    return ret;
}

int main() {
    cin >> n >> m;
    int u, v;
    for(int i=0;i<m;++i) {
        cin >> u >> v;
        --u; --v;
        edges.push_back(Edge(u, v, 1));
        G[u].push_back(v);
    }

    vector<int> ordered_nodes = topological_sort();
    vector<int> c(m, 1);
    if(ordered_nodes.size() == n) {
        k = 1;
    } else {
        k = 2;
        for(int i=0;i<m;++i) {
            if(edges[i].u > edges[i].v) c[i] = 2;
        }
    }

    cout << k << endl;
    for(int i=0;i<m-1;++i) cout << c[i] << " ";
    cout << c[m-1] << endl;
}

所感

ブログを更新しない間に AtCoder のレートが破滅していました.心当たりはいろいろありますが,そのうち1つが「解説ブログの更新をサボっていた」なので今週からまた1週間1記事以上を目標に頑張ります.