#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
const int MAXM = 3e5 + 5;
int T = 566;
struct disjoint_set{
    vector<int> lab;
    disjoint_set(int n) : lab(n, -1) {}
    int root(int u){
        return lab[u] < 0 ? u : (lab[u] = root(lab[u]));
    }
    bool join(int u, int v){
        u = root(u);
        v = root(v);
        if(u == v) return false;
        if(lab[u] > lab[v]) swap(u, v);
        lab[u] += lab[v];
        lab[v] = u;
        return true;
    }
} dsu(0);
struct dsu_tree{
    int used;
    vector<int> lab, depth, pos, value, tour;
    vector<vector<int>> adj, rmq;
    dsu_tree(int n) : lab(2 * n, -1), rmq(), tour(), depth(2 * n, 0), pos(2 * n, 0), adj(2 * n), value(2 * n, -1), used(n) {}
    int root(int u){
        return lab[u] < 0 ? u : lab[u] = root(lab[u]);
    }
    void add_edge(int u, int v, int label){
        u = root(u);
        v = root(v);
        assert(u != v);
        lab[used] = -1;
        value[used] = label;
        adj[used].emplace_back(u);
        adj[used].emplace_back(v);
        lab[u] = used;
        lab[v] = used;
        ++used;
    }
    void dfs(int u){
        pos[u] = (int)tour.size();
        tour.emplace_back(u);
        for(auto v : adj[u]){
            depth[v] = depth[u] + 1;
            dfs(v);
            tour.emplace_back(u);
        }
    }
    int combine(int u, int v){
        return depth[u] < depth[v] ? u : v;
    }
    void process(){
        dfs(used - 1);
        int sz = (int)tour.size();
        int L = 32 - __builtin_clz(sz);
        rmq.resize(L);
        rmq[0] = tour;
        for(int i = 1; i < L; ++i){
            rmq[i].resize(sz - (1 << i) + 1);
            for(int j = 0; j + (1 << i) <= sz; ++j){
                rmq[i][j] = combine(rmq[i - 1][j], rmq[i - 1][j + (1 << (i - 1))]);
            }
        }
    }
    int get_lca(int u, int v){
        u = pos[u];
        v = pos[v];
        if(u > v) swap(u, v);
        int k = 31 - __builtin_clz(v - u + 1);
        return combine(rmq[k][u], rmq[k][v - (1 << k) + 1]);
    }
    int get_label(int u, int v){
        return value[get_lca(u, v)];
    }
    int size() { return (int)used; }
} tr(0);
int N, depth[MAXN], tin[MAXN], tout[MAXN], timer_dfs, timer_rmq, pos[MAXN], rmq[20][MAXN * 2];
vector<pair<int, int>> adj[MAXN];
vector<int> computed_answer[MAXN];
vector<int> nodes[MAXN];
void dfs(int u, int p){
    tin[u] = ++timer_dfs;
    rmq[0][pos[u] = timer_rmq++] = u;
    for(auto [v, w] : adj[u]) if(v != p){
        depth[v] = depth[u] + 1;
        dfs(v, u);
        rmq[0][timer_rmq++] = u;
    }
    tout[u] = timer_dfs;
}
int combine(int u, int v){ return depth[u] < depth[v] ? u : v; }
void preprocessRMQ(){
    for(int i = 1; (1 << i) <= timer_rmq; ++i){
        for(int j = 0; j + (1 << i) <= timer_rmq; ++j){
            rmq[i][j] = combine(rmq[i - 1][j], rmq[i - 1][j + (1 << (i - 1))]);
        }
    }
}
bool in_subtree(int u, int v){
    return tin[u] <= tin[v] && tout[v] <= tout[u];
}
int get_lca(int u, int v){
    u = pos[u];
    v = pos[v];
    if(u > v) swap(u, v);
    int k = 31 - __builtin_clz(v - u + 1);
    return combine(rmq[k][u], rmq[k][v - (1 << k) + 1]);
}
struct query_dsu{
    vector<int> lab;
    vector<vector<int>> cnt;
    query_dsu(int n) : lab(n, -1), cnt(n, vector<int>(3, 0)) {}
    int root(int u){
        return lab[u] < 0 ? u : lab[u] = root(lab[u]);
    }
    bool unite(int u, int v){
        u = root(u);
        v = root(v);
        if(u == v) return false;
        if(lab[u] > lab[v]) swap(u, v);
        lab[u] += lab[v];
        lab[v] = u;
        for(int i = 0; i < 3; ++i) cnt[u][i] += cnt[v][i];
        return true;
    }
    bool meet(int u){
        u = root(u);
        return cnt[u][1] && cnt[u][2];
    }
    void reset(int u){
        lab[u] = -1;
        cnt[u][0] = cnt[u][1] = cnt[u][2] = 0;
    }
} slv(0);
void Init(int K, vector<int> F, vector<int> S, vector<int> E){
    int N = (int)F.size();
    int M = (int)S.size();
    for(int i = 0; i < N; ++i) nodes[F[i]].emplace_back(i);
    dsu = disjoint_set(N);
    tr = dsu_tree(N);
    for(int i = M - 1; i >= 0; --i){
        if(dsu.join(S[i], E[i])){
            adj[S[i]].emplace_back(E[i], i + 1);
            adj[E[i]].emplace_back(S[i], i + 1);
            tr.add_edge(S[i], E[i], i + 1);
        } else assert(false);
    }
    dfs(0, -1);
    preprocessRMQ();
    tr.process();
    assert(tr.size() == 2 * N - 1);
    function<void(int, vector<int>&)> solve_for = [&](int c, vector<int>& ans){
        vector<bool> deleted(N, false);
        priority_queue<pair<int, int>> pq;
        vector<int> assignment(N, -1);
        for(auto it : nodes[c]) {
            assignment[it] = M;
            pq.push({M, it});
        }
        while(!pq.empty()){
            int mn, u;
            tie(mn, u) = pq.top(); pq.pop();
            if(deleted[u]) continue;
            deleted[u] = true;
            ans[F[u]] = max(ans[F[u]], assignment[u]);
            for(auto [v, w] : adj[u]){
                if(!deleted[v] && assignment[v] < min(mn, w)){
                    assignment[v] = min(mn, w);
                    pq.push({assignment[v], v});
                }
            }
        }
    };
    for(int i = 0; i < K; ++i){
        assert(!nodes[i].empty());
        if((int)nodes[i].size() > T){
            computed_answer[i].resize(K);
            solve_for(i, computed_answer[i]);
        }
    }
    slv = query_dsu(N);
}
int solve_virtual_tree(vector<int> A, vector<int> B){
    vector<int> vert = A;
    vert.insert(vert.end(), B.begin(), B.end());
    sort(vert.begin(), vert.end(), [&](int u, int v){
        return tin[u] < tin[v];
    });
    vert.erase(unique(vert.begin(), vert.end()), vert.end());
    int cur = (int)vert.size();
    for(int i = 1; i < cur; ++i){
        vert.emplace_back(get_lca(vert[i - 1], vert[i]));
    }
    sort(vert.begin(), vert.end(), [&](int u, int v){
        return tin[u] < tin[v];
    });
    vert.erase(unique(vert.begin(), vert.end()), vert.end());
    for(auto u : vert){
        if(binary_search(A.begin(), A.end(), u)) slv.cnt[u][1] = 1;
        if(binary_search(B.begin(), B.end(), u)) slv.cnt[u][2] = 1;
    }
    vector<tuple<int, int, int>> edges;
    stack<int> st;
    st.push(vert[0]);
    cur = (int)vert.size();
    for(int i = 1; i < cur; ++i){
        while(!in_subtree(st.top(), vert[i])){
            st.pop();
        }
        assert(!st.empty());
        edges.emplace_back(tr.get_label(st.top(), vert[i]), st.top(), vert[i]);
        st.push(vert[i]);
    }
    sort(edges.rbegin(), edges.rend());
    int ans = -1;
    for(auto [w, u, v] : edges){
        slv.unite(u, v);
        if(slv.meet(u)){
            ans = w;
            break;
        }
    }
    assert(ans != -1);
    for(auto u : vert){
        slv.reset(u);
    }
    return ans;
}
int Separate(int A, int B){
    if((int)nodes[A].size() < (int)nodes[B].size()) swap(A, B);
    if((int)nodes[A].size() > T){
        return computed_answer[A][B];
    }
    return solve_virtual_tree(nodes[A], nodes[B]);
}
#ifdef LOCAL
int main(){
    ios_base::sync_with_stdio(0); cin.tie(0);
    freopen("task.inp", "r", stdin);
    freopen("task.out", "w", stdout);
    int N, M, K, Q;
    cin >> N >> M >> K >> Q;
    vector<int> F(N);
    for(int i = 0; i < N; ++i) cin >> F[i];
    vector<int> S(M), E(M);
    for(int i = 0; i < M; ++i) cin >> S[i] >> E[i];
    Init(K, F, S, E);
    while(Q--){
        int A, B;
        cin >> A >> B;
        cout << Separate(A, B) << '\n';
    }
    return 0;
}
#endif //LOCAL
| # | Verdict  | Execution time | Memory | Grader output | 
|---|
| Fetching results... | 
| # | Verdict  | Execution time | Memory | Grader output | 
|---|
| Fetching results... | 
| # | Verdict  | Execution time | Memory | Grader output | 
|---|
| Fetching results... |