haraduka's diary

やる気が欲しい

ARC039 D問題 旅行会社高橋君

考え方は簡単。というより一瞬で気づいた。
ただ、この問題、二重辺連結成分分解、まぁつまり橋の検出と、LowestCommonAncesterの実装をしないといけないのでライブラリとか持ってないと地獄…つらかった…。
まず、a,b,cという始点,中継点,終点が与えられたときa,b またはb,cが同じ二重辺連結成分に入っていればどう考えてもおk
入っていないときは、二重辺連結成分分解でただの木にしたあと、a,cの間にbが存在すればよいということになる。つまり、aからbの距離とbからcの距離の和がaからcの距離の和になっていればいいのである。これをするにはLCAを使うのじゃ〜〜
二重辺連結成分分解はlowlink理解してれば簡単。LCAもまぁそこまで難しい話じゃないので、理解してスパゲッティコードと蟻本からとってきた。

struct Edge {
    int src, dst;
    Edge(int src, int dst) : src(src), dst(dst) {}
};

typedef vector<Edge> Edges;
typedef vector<Edges> Graph;

void visit(const Graph & g, int v, int u,
        Edges& brdg, vector< vector<int> >& tecomp,
        stack<int>& roots, stack<int>& S, vector<bool>& inS,
        vector<int>& num, int& time) {
    num[v] = ++time;
    S.push(v); inS[v] = true;
    roots.push(v);
    for (auto e : g[v]) {
        int w = e.dst;
        if (num[w] == 0) //non-used
            visit(g, w, v, brdg, tecomp, roots, S, inS, num, time);
        else if (u != w && inS[w])
            while (num[roots.top()] > num[w]) roots.pop();
    }
    if (v == roots.top()) {
        brdg.push_back(Edge(u, v));
        tecomp.push_back(vector<int>());
        while (true) {
            int w = S.top(); S.pop(); inS[w] = false;
            tecomp.back().push_back(w);
            if (v == w) break;
        }
        roots.pop();
    }
}

void bridge(const Graph& g, Edges& brdg, vector< vector<int> >& tecomp) {
    const int n = (int)g.size();
    vector<int> num(n);
    vector<bool> inS(n);
    stack<int> roots, S;
    int time = 0;
    for (int u = 0; u < n; u++) if (num[u] == 0) {
        visit(g, u, n, brdg, tecomp, roots, S, inS, num, time);
        brdg.pop_back();
    }
}

template<int V>
struct LowestCommonAncestor {
    const static int LG = 25; //頂点の数のlogだけどVが2^25を超えることないしこれでよさ気
    int ro[LG][V]; //ro[i][j]で、頂点jの2^i親にあたる頂点を指す
    int dps[V]; //根からの深さ
    vector<int> g[V];
    /// i-jに有向辺を張る
    void add(int i, int j) {
        g[i].push_back(j);
        g[j].push_back(i);
    }
    //initで呼ばれるので無視して
    void dfs(int p, int b) {
        assert(ro[0][p] == -1);
        ro[0][p] = b;
        dps[p] = (b == -1) ? 0 : dps[b]+1;
        for (int d: g[p]) {
            if (d == b) continue;
            dfs(d, p);
        }
    }
    /// 事前処理を行う rはroot頂点のid
    void init(int r) {
        memset(ro, -1, sizeof(ro));
        dfs(r, -1);
        for (int i = 0; i < LG-1; i++) {
            for (int j = 0; j < V; j++) {
                ro[i+1][j] = (ro[i][j] == -1) ? -1 : ro[i][ro[i][j]];
            }
        }
    }
    /// lとrの頂点のLCAを求める
    int query(int l, int r) const {
        if (dps[l] < dps[r]) swap(l, r);
        int dd = dps[l]-dps[r];
        for (int i = LG-1; i >= 0; i--) {
            if (dd < (1<<i)) continue;
            dd -= 1<<i;
            l = ro[i][l];
        }
        if (l == r) return l;
        for (int i = LG-1; i >= 0; i--) {
            if (ro[i][l] == ro[i][r]) continue;
            l = ro[i][l]; r = ro[i][r];
        }
        return ro[0][l];
    }
    int dist(int u, int v) const {
        int p = query(u, v);
        return (dps[u]-dps[p]) + (dps[v]-dps[p]);
    }
};

const int MAXN = 100010;
int A[MAXN];
LowestCommonAncestor<MAXN> LCA;

int main()
{
    cin.tie(0);
    ios::sync_with_stdio(false);

    int N,M;
    cin >> N >> M;
    Graph g(N);
    for(int i=0; i<M; i++){
        int x, y;
        cin >> x >> y;
        x--; y--;
        g[x].push_back(Edge(x, y));
        g[y].push_back(Edge(y, x));
    }
    Edges brdg;
    vector< vector<int> > tecomp;
    bridge(g, brdg, tecomp);
    for(int i=0; i<len(tecomp); i++){
        for(auto el : tecomp[i]){
            A[el] = i;
        }
    }
    for(int i=0; i<len(brdg); i++){
        LCA.add(A[brdg[i].src], A[brdg[i].dst]);
    }
    LCA.init(0);
    int Q; cin >> Q;
    for(int i=0; i<Q; i++){
        int a, b, c;
        cin >> a >> b >> c;
        a--; b--; c--;
        if(A[a] == A[b] || A[b] == A[c]){
            cout << "OK" << endl;
            continue;
        }
        if(LCA.dist(A[a], A[b]) + LCA.dist(A[b], A[c]) == LCA.dist(A[a], A[c])){
            cout << "OK" << endl;
            continue;
        }
        cout << "NG" << endl;
    }
}