Farm Updates (USACO Gold 2022 January)

Farm Updates (USACO Gold 2022 January)

关键条件是加边只在active的farm间,所以这里的加边操作不会改变任何farm是否relevant,所以可以倒过来处理。使用union-find,只有R、D事件影响relevancy,R操作将连通分量合并,D操作可能将所在连通分量全部变成relevant的。

具体实现细节比较多:

  1. 没有disable的节点要记得处理
  2. 没有remove的边要记得处理
  3. 并查集需要保存连通分量里的节点列表(这里保存了非relevant的节点列表inact[],保存全部节点列表也行),但合并时需要把小的连通分量合并到大的,否则节点列表复制会导致10以后case TLE.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
 
// n <= 1e5, Q <= 2e5
int n, Q;
int ans[100005];
struct E {
    char t;       // D, A, R
    int i,j;      // (i,j) for add edge, i for R or D
};
E es[200005];
vector<E> edges;
bool a[100005];     // active vertex
vector<bool> e0;    // edges that are not removed over the whole course
int fa[100005];
vector<int> inact[100005];      // inactive vertices for each component
 
int find(int x) {
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}
 
void add_edge(int x, int q) {
    int u = find(edges[x].i), v = find(edges[x].j);   // merge u and v
    if (u == v) return;
    if (!a[u] && a[v] || a[u] && !a[v]) {    // one active
        fa[u] = v;
        if (!a[u]) swap(u,v);   // u active, v inactive
        for (int i: inact[v]) {
            // printf("linking %d and %d: setting [%d] to %d\n", u, v, i, q-1);
            ans[i] = q-1;
        }
        a[v] = true;
        inact[v].clear();
    } else {
        if (!a[u] && !a[v]) {
            if (inact[u].size() > inact[v].size()) swap(u,v);   // add smaller to larger
            for (int p: inact[u]) inact[v].push_back(p);
            inact[u].clear();
        }
        fa[u] = v;
    }
}
 
int main() {
    scanf("%d %d", &n, &Q);
    fill(a, a+n+1, true);
    edges.push_back({'A',0,0});     // make edges 1-based
    e0.push_back(true);
    for (int i = 1; i <= Q; i++) {
        char s[10];
        scanf("%s %d", s, &es[i].i);            // event list
        es[i].t = s[0];
        if (s[0] == 'A') {
            scanf("%d", &es[i].j);              
            edges.push_back(es[i]);             // build edge list
            e0.push_back(true);
        }
        if (s[0] == 'D') a[es[i].i] = false;    // build initial active list
        if (s[0] == 'R') e0[es[i].i] = false;
    }
 
    // execute events backwards and apply union-find
    for (int i = 1; i <= n; i++) {
        fa[i] = i;
        if (a[i]) ans[i] = Q;           // add all initial vertices
        else inact[i].push_back(i);     
    }
    for (int i = 1; i < e0.size(); i++)
        if (e0[i]) add_edge(i, Q+1);    // add all initial edges
    for (int q = Q; q >= 1; q--) {
        E e = es[q];
        if (e.t == 'D') {
            // activate all vertices in component
            int u = find(e.i);
            if (!a[u]) {
                a[u] = true;
                for (int i: inact[u]) ans[i] = q-1;
                inact[u].clear();
            }
        } else if (e.t == 'R') {
            add_edge(e.i, q);
        }
    }
 
    for (int i = 1; i <= n; i++)
        printf("%d\n", ans[i]);
 
    return 0;
}