結果

問題 No.922 東北きりきざむたん
ユーザー tomatoma
提出日時 2019-11-08 23:28:46
言語 C++14
(gcc 13.2.0 + boost 1.83.0)
結果
AC  
実行時間 341 ms / 2,000 ms
コード長 6,957 bytes
コンパイル時間 2,727 ms
コンパイル使用メモリ 209,064 KB
実行使用メモリ 33,108 KB
最終ジャッジ日時 2023-10-13 05:08:58
合計ジャッジ時間 8,971 ms
ジャッジサーバーID
(参考情報)
judge11 / judge14
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 1 ms
4,348 KB
testcase_01 AC 1 ms
4,348 KB
testcase_02 AC 2 ms
4,348 KB
testcase_03 AC 1 ms
4,348 KB
testcase_04 AC 3 ms
4,352 KB
testcase_05 AC 1 ms
4,348 KB
testcase_06 AC 3 ms
4,348 KB
testcase_07 AC 3 ms
4,352 KB
testcase_08 AC 2 ms
4,352 KB
testcase_09 AC 157 ms
17,268 KB
testcase_10 AC 78 ms
6,716 KB
testcase_11 AC 129 ms
13,504 KB
testcase_12 AC 136 ms
22,048 KB
testcase_13 AC 53 ms
8,016 KB
testcase_14 AC 304 ms
26,800 KB
testcase_15 AC 112 ms
24,020 KB
testcase_16 AC 277 ms
21,800 KB
testcase_17 AC 286 ms
21,928 KB
testcase_18 AC 280 ms
21,732 KB
testcase_19 AC 278 ms
21,668 KB
testcase_20 AC 279 ms
21,600 KB
testcase_21 AC 251 ms
21,088 KB
testcase_22 AC 249 ms
20,836 KB
testcase_23 AC 221 ms
20,368 KB
testcase_24 AC 221 ms
20,104 KB
testcase_25 AC 183 ms
21,552 KB
testcase_26 AC 185 ms
21,428 KB
testcase_27 AC 183 ms
21,368 KB
testcase_28 AC 341 ms
28,856 KB
testcase_29 AC 196 ms
33,108 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#include"bits/stdc++.h"
using namespace std;
#define REP(k,m,n) for(int (k)=(m);(k)<(n);(k)++)
#define rep(i,n) REP((i),0,(n))
using ll = long long;


class UnionFind {
public:
    vector<int>rank, parent;
    //初期化
    UnionFind(int size) {
        rank.resize(size, 0);
        parent.resize(size, 0);
        rep(i, size)parent[i] = i;
    }
    //木の根を求める
    int find(int x) {
        if (parent[x] == x)return x;
        else return parent[x] = find(parent[x]);
    }
    //xとyの属する集合を併合
    void unite(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y)return;
        if (rank[x] < rank[y])
            parent[x] = y;
        else {
            parent[y] = x;
            if (rank[x] == rank[y])rank[x]++;
        }
    }
    //xとyが同じ集合に属するか否か
    bool same(int x, int y) {
        return (find(x) == find(y));
    }
};



vector<vector<int>> edges;
vector<int> weight;
vector<pair<int, int>> sizeSubtree; // {コスト和, 頂点の数}
UnionFind uf(0);

map<int, int> tot;
map<int, int>cent;

void SubFindCentroids(int v, int parent_of_v = -1) {
    const int N = tot[uf.find(v)];
    sizeSubtree[v] = { 0,weight[v] };

    for (auto ch : edges[v]) {
        if (ch == parent_of_v) continue;
        SubFindCentroids(ch, v);
        auto res = sizeSubtree[ch];
        res.first += res.second;
        sizeSubtree[v].first += res.first;
        sizeSubtree[v].second += res.second;
    }
}

int findCent(int now) {
    int cost, cnt;
    tie(cost, cnt) = sizeSubtree[now];
    int w = weight[now];

    int bcost = cost;
    int bpos = now;
    for (int next : edges[now]) {
        int ncost, ncnt;
        tie(ncost, ncnt) = sizeSubtree[next];
        ncost = (cost - ncnt) + (cnt - ncnt);
        if (ncost < bcost) {
            bcost = ncost;
            bpos = next;
        }
    }
    if (now == bpos)return now;
    int ncost, ncnt;
    tie(ncost, ncnt) = sizeSubtree[bpos];
    sizeSubtree[now] = { cost - ncost - ncnt,cnt - ncnt };
    sizeSubtree[bpos] = { bcost, tot[uf.find(now)] };
    return findCent(bpos);
}


using Graph = vector<vector<int>>;
struct HLDecomposition {
    using pii = pair<int, int>;
    int n;
    Graph G;
    vector<int> vid, inv, par, depth, subsize, head, prev, next, type;

    HLDecomposition(const Graph& G_) :
        n(G_.size()), G(G_),
        vid(n, -1), inv(n), par(n), depth(n), subsize(n, 1),
        head(n), prev(n, -1), next(n, -1), type(n) {}
    void build(vector<int> roots = { 0 }) {
        int curtype = 0, pos = 0;
        for (int root : roots) {
            decide_heavy_edge(root);
            reconstruct(root, curtype++, pos);
        }
    }
    void decide_heavy_edge(int root) {
        stack<pii> st;
        par[root] = -1, depth[root] = 0;
        st.emplace(root, 0);
        while (!st.empty()) {
            int now = st.top().first;
            int& way = st.top().second;
            if (way < G[now].size()) {
                int child = G[now][way++];
                if (child == par[now])continue;
                par[child] = now;
                depth[child] = depth[now] + 1;
                st.emplace(child, 0);
            }
            else {
                st.pop();
                int maxsize = 0;
                for (auto child : G[now]) {
                    if (child == par[now])continue;
                    subsize[now] += subsize[child];
                    if (maxsize < subsize[child]) {
                        maxsize = subsize[child];
                        prev[child] = now;
                        next[now] = child;
                    }
                }
            }
        }
    }
    void reconstruct(int root, int curtype, int& pos) {
        stack<int> st({ root });
        while (!st.empty()) {
            int start = st.top(); st.pop();
            for (int v = start; v != -1; v = next[v]) {
                type[v] = curtype;
                vid[v] = pos++;
                inv[vid[v]] = v;
                head[v] = start;
                for (auto child : G[v]) {
                    if (child != par[v] && child != next[v]) {
                        st.push(child);
                    }
                }
            }
        }
    }

    // node query [u, v], f([left, right])
    void foreach_nodes(int u, int v, const function<void(int, int)>& f) {
        while (true) {
            if (vid[u] > vid[v])swap(u, v);
            f(max(vid[head[v]], vid[u]), vid[v]);
            if (head[u] != head[v])v = par[head[v]];
            else break;
        }
    }

    // edge query[u,v] f([left, right])
    // seg_node[vid[i]] := edge(par[i] -> i)
    void foreach_edges(int u, int v, const function<void(int, int)>& f) {
        while (true) {
            if (vid[u] > vid[v])swap(u, v);
            if (head[u] != head[v]) {
                f(vid[head[v]], vid[v]);
                v = par[head[v]];
            }
            else {
                if (u != v)f(vid[u] + 1, vid[v]);
                break;
            }
        }
    }
    int lca(int u, int v) {
        while (true) {
            if (vid[u] > vid[v])swap(u, v);
            if (head[u] == head[v])return u;
            v = par[head[v]];
        }
    }
};


int main()
{
    int N, M, Q;
    cin >> N >> M >> Q;
    edges.resize(N);
    weight.resize(N);
    uf = UnionFind(N);

    sizeSubtree.resize(N);
    rep(i, M) {
        int u, v;
        cin >> u >> v;
        u--; v--;
        edges[u].push_back(v);
        edges[v].push_back(u);
        uf.unite(u, v);
    }

    vector<pair<int, int>> ab;
    rep(i, Q) {
        int a, b;
        cin >> a >> b;
        a--; b--;
        ab.push_back({ a,b });
        if (!uf.same(a, b)) {
            weight[a]++;
            weight[b]++;
            tot[uf.find(a)]++;
            tot[uf.find(b)]++;
        }
    }

    {
        set<int> st;
        rep(i, N) {
            int par = uf.find(i);
            if (st.find(par) != st.end())continue;
            st.insert(par);
            SubFindCentroids(i);
            cent[par] = findCent(i);
        }
    }

    HLDecomposition hld(edges);
    {
        set<int> st;
        rep(i, N)st.insert(uf.find(i));
        vector<int> routes;
        for (int num : st)routes.push_back(num);
        hld.build(routes);
    }

    ll res = 0;
    for (auto route : ab) {
        int a, b;
        tie(a, b) = route;
        if (uf.same(a, b)) {
            int m = hld.lca(a, b);
            res += hld.depth[a] + hld.depth[b] - 2 * hld.depth[m];
        }
        else {
            int from = cent[uf.find(a)];
            assert(uf.same(a, from));
            int m = hld.lca(a, from);
            res += hld.depth[a] + hld.depth[from] - 2 * hld.depth[m];

            int to = cent[uf.find(b)];
            m = hld.lca(b, to);
            res += hld.depth[b] + hld.depth[to] - 2 * hld.depth[m];
        }
    }
    cout << res << endl;
    return 0;
}
0