algorithm

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub drken1215/algorithm

:warning: 列挙可能 Union-Find の例 (ABC 328 F)
(tests/ABC_328_F_enumerable_union_find.cpp)

概要

「列挙可能 Union-Find」の使用例である。

“union_find.hpp” に列挙機能を搭載している。

ABC 328 F の問題概要

$𝑁$ 個の整数値 $𝑥_1, 𝑥_2, \dots, 𝑥_𝑁$ に関する制約条件が $𝑄$ 個与えられる。

$𝑖$ 番目の制約条件では 3 つの整数の組 $(𝑎,𝑏,𝑑)$ が与えられ、$𝑥_𝑎 − 𝑥_𝑏 = 𝑑$ という形をしている。ここで、次のクエリに答えよ。

すでにある制約条件と、この $𝑖$ 番目の制約条件「$𝑥_𝑎 − 𝑥_𝑏 = 𝑑$」とが矛盾するかどうかを判定せよ。 矛盾しない場合には $𝑖$ を出力し、矛盾する場合には何も出力しないこととする。

ABC 328 F の解法

制約条件 $𝑥_𝑎 − 𝑥_𝑏 = 𝑑$ によって要素 $𝑎,𝑏$ をマージする際に、根付き木のサイズが小さい方の各頂点について、$𝑥$ の値を調整する。

マージテクの仕組みによって、全体の計算量は $𝑂⁢(𝑁 + 𝑄⁢ \log⁡ 𝑁)$ となる。

別解

ポテンシャル付き Union-Find を用いる解法もある

参考文献

Depends on

Code

#define PROBLEM "https://atcoder.jp/contests/abc328/tasks/abc328_f"

#include "union_find.hpp"

int main() {
    int N, Q;
    cin >> N >> Q;
    UnionFind uf(N);
    vector<long long> x(N, 0);  // x value
    for (int i = 0; i < Q; ++i) {
        int a, b, d;a
        cin >> a >> b >> d;
        --a, --b;
        
        bool good = true;
        if (uf.same(a, b)) {
            // x[a] - x[b] = d でないとき、ダメ
            if (x[a] - x[b] != d) good = false;
        } else {
            // マージテクにより、size(a) < size(b) となるようにする
            if (uf.size(a) > uf.size(b)) {
                swap(a, b);
                d = -d;
            }
            
            // a を含むグループのメンバーたちに足す値を求める
            long long add = (x[b] + d) - x[a];
            
            // x の値を調整して、マージする
            vector<int> ids = uf.group(a);
            for (int id : ids) x[id] += add;
            uf.merge(a, b);
        }

        // good ならば出力する
        if (good) cout << i+1 << " ";
    }
    cout << endl;
}
#line 1 "tests/ABC_328_F_enumerable_union_find.cpp"
#define PROBLEM "https://atcoder.jp/contests/abc328/tasks/abc328_f"

#line 2 "DataStructure/union_find.hpp"
#include <bits/stdc++.h>
using namespace std;

// Union-Find
struct UnionFind {
    // core member
    vector<int> par, nex;

    // constructor
    UnionFind() { }
    UnionFind(int N) : par(N, -1), nex(N) {
        init(N);
    }
    void init(int N) {
        par.assign(N, -1);
        nex.resize(N);
        for (int i = 0; i < N; ++i) nex[i] = i;
    }
    
    // core methods
    int root(int x) {
        if (par[x] < 0) return x;
        else return par[x] = root(par[x]);
    }
    
    bool same(int x, int y) {
        return root(x) == root(y);
    }
    
    bool merge(int x, int y) {
        x = root(x), y = root(y);
        if (x == y) return false;
        if (par[x] > par[y]) swap(x, y); // merge technique
        par[x] += par[y];
        par[y] = x;
        swap(nex[x], nex[y]);
        return true;
    }
    
    int size(int x) {
        return -par[root(x)];
    }
    
    // get group
    vector<int> group(int x) {
        vector<int> res({x});
        while (nex[res.back()] != x) res.push_back(nex[res.back()]);
        return res;
    }
    vector<vector<int>> groups() {
        vector<vector<int>> member(par.size());
        for (int v = 0; v < (int)par.size(); ++v) {
            member[root(v)].push_back(v);
        }
        vector<vector<int>> res;
        for (int v = 0; v < (int)par.size(); ++v) {
            if (!member[v].empty()) res.push_back(member[v]);
        }
        return res;
    }
    
    // debug
    friend ostream& operator << (ostream &s, UnionFind uf) {
        const vector<vector<int>> &gs = uf.groups();
        for (const vector<int> &g : gs) {
            s << "group: ";
            for (int v : g) s << v << " ";
            s << endl;
        }
        return s;
    }
};
#line 4 "tests/ABC_328_F_enumerable_union_find.cpp"

int main() {
    int N, Q;
    cin >> N >> Q;
    UnionFind uf(N);
    vector<long long> x(N, 0);  // x value
    for (int i = 0; i < Q; ++i) {
        int a, b, d;a
        cin >> a >> b >> d;
        --a, --b;
        
        bool good = true;
        if (uf.same(a, b)) {
            // x[a] - x[b] = d でないとき、ダメ
            if (x[a] - x[b] != d) good = false;
        } else {
            // マージテクにより、size(a) < size(b) となるようにする
            if (uf.size(a) > uf.size(b)) {
                swap(a, b);
                d = -d;
            }
            
            // a を含むグループのメンバーたちに足す値を求める
            long long add = (x[b] + d) - x[a];
            
            // x の値を調整して、マージする
            vector<int> ids = uf.group(a);
            for (int id : ids) x[id] += add;
            uf.merge(a, b);
        }

        // good ならば出力する
        if (good) cout << i+1 << " ";
    }
    cout << endl;
}
Back to top page