ferinの競プロ帳

競プロについてのメモ

Codeforces Round #600 (Div. 2) F - Cheap Robot

問題ページ
ボロノイMSTの要領でcentralが頂点となっていて辺の重みが元のグラフのcentral間の最短距離であるようなグラフを構成する.
capacityが c のときにクエリ A_i,B_i を達成可能か? \iff グラフで A_iB_i のパスの重みのmaxがc以下 となる.c を決め打つと,使用できる辺が決まる.このときに A_iB_i が連結であれば容量が c 以上のとき達成可能である.c について二分探索をすれば,各クエリについて O(KlogK) で答えがわかる.
あとは並列二分探索や永続union-findなどを使うことでグラフを複数回構築している部分をまとめることができ,十分高速に答えを求められる.

以下のコードは部分永続UFを使ったもの

#include <bits/stdc++.h>  
using namespace std;  
using ll = long long;  
using PII = pair<ll, ll>;  
#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)  
#define REP(i, n) FOR(i, 0, n)  
#define ALL(x) x.begin(), x.end()  
template<typename T> void chmin(T &a, const T &b) { a = min(a, b); }  
template<typename T> void chmax(T &a, const T &b) { a = max(a, b); }  
struct FastIO {FastIO() { cin.tie(0); ios::sync_with_stdio(0); }}fastiofastio;  
#ifdef DEBUG_   
#include "../program_contest_library/memo/dump.hpp"  
#else  
#define dump(...)  
#endif  
const ll INF = 1LL<<60;  
  
// find:O(logN) unite:O(logN)  
struct partialPersistentUF {  
    const static int MAX_N = 100010;  
    unordered_map<int, int> par[MAX_N];  
    int rank[MAX_N];  
    int fin[MAX_N];  
    int idx;  
    partialPersistentUF() { init(); }  
    partialPersistentUF(int n) { init(n); }  
    void init(int n=MAX_N) {  
        idx = 0;  
        REP(i, n) par[i][0] = i, rank[i] = 1, fin[i] = 0;  
    }  
    // t(1-indexed)回目までのuniteでのxの親  
    int find(int x, int t) {  
        if(t >= fin[x] && par[x][fin[x]] != x) return find(par[x][fin[x]], t);  
        return x;  
    }  
    void unite(int x, int y) {  
        x = find(x, idx);  
        y = find(y, idx);  
        idx++;  
        if(x == y) return;  
        if(rank[x] < rank[y]) par[x][idx] = y, fin[x] = idx;  
        else {  
          par[y][idx] = x, fin[y] = idx;  
          if(rank[x] == rank[y]) rank[x]++;  
        }  
    }  
    bool same(int x, int y, int t) { return find(x, t) == find(y, t); }  
};  
  
int main(void) {  
    ll n, m, k, q;  
    cin >> n >> m >> k >> q;  
    vector<vector<PII>> g(n);  
    REP(i, m) {  
        ll a, b, c;  
        cin >> a >> b >> c;  
        a--, b--;  
        g[a].push_back({b, c});  
        g[b].push_back({a, c});  
    }  
  
    // central から dijkstra  
    vector<ll> col(n, -1), dist(n, INF);  
    priority_queue<PII, vector<PII>, greater<PII>> que;  
    REP(i, k) {  
        col[i] = i;  
        dist[i] = 0;  
        que.push({0, i});  
    }  
    while(que.size()) {  
        PII p = que.top(); que.pop();  
        if(dist[p.second] < p.first) continue;  
        for(auto to: g[p.second]) {  
            if(dist[to.first] > dist[p.second] + to.second) {  
                dist[to.first] = dist[p.second] + to.second;  
                col[to.first] = col[p.second];  
                que.push({dist[to.first], to.first});  
            }  
        }  
    }  
    // 重み dist[i]+dist[to.first]+to.second で col[i] と col[to.first] を結ぶ辺
    map<ll, vector<PII>> mp;  
    REP(i, n) for(auto to: g[i]) {  
        if(i < to.first && col[i] != col[to.first]) {  
            mp[dist[i]+dist[to.first]+to.second].push_back({col[i], col[to.first]});  
        }  
    }  
    // UFの処理  
    ll sz = 0;  
    vector<ll> ts, ope;  
    partialPersistentUF uf(k);  
    for(auto i: mp) {  
        ts.push_back(i.first);  
        for(auto j: i.second) uf.unite(j.first, j.second);  
        sz += i.second.size();  
        ope.push_back(sz);  
    }  
  
    while(q--) {  
        ll a, b;  
        cin >> a >> b;  
        a--, b--;  
  
        ll lb = -1, ub = (ll)ts.size()-1;  
        while(ub - lb > 1) {  
            ll mid = (lb+ub)/2;  
            if(uf.same(a, b, ope[mid])) ub = mid;  
            else lb = mid;  
        }  
        cout << ts[ub] << '\n';  
    }  
  
    return 0;  
}