2022牛客多校第8场 I.Equivalence in Connectivity

题目大意

给定一张 \(n\) 个点 \(m\) 条边的无向图,定义两张图 \(G_1\)\(G_2\) 连通性等价,当且仅当 \(\forall u,v\in G_1\),只要在 \(G_1\)\(u\)\(v\) 连通,一定有 \(G_2\)\(u\)\(v\) 连通。再给出 \(k-1\) 次操作,每次操作都会继承之前的某张图,添加或删除一条边,形成一张新图,由此得到 \(k\) 张图。求这 \(k\) 张图的连通性等价类。

题解

在原图上添加或删除一条边,形成一张新图,这一操作形成一棵树。求出这棵树的DFS序,将树转化为序列。每条边都只在DFS序上的若干段区间上存在,于是可以在DFS序上做线段树分治。如果没有撤销操作,每次加入的一条边覆盖了某一棵子树内的所有结点这一DFS序区间。观察得每添加一次撤销操作,最多只会将区间分裂成两半,即最多只会新覆盖1个区间,最终覆盖的区间数量级是\(O(m+k)\),每个区间只会横跨线段树上 \(O(\log k)\) 个区间。两张图联通性等价,实际上只要连通块相同即可。可以使用并查集维护连通块,然后再辅以某种哈希方法,来求得每张图的哈希值,就能求得最终的连通性等价类。在线段树分治中,必须是可撤销并查集,以支持回溯,因此只能按秩合并,不能路径压缩,并查集单次操作复杂度 \(O(\log n)\)。对于哈希方法,这里使用双哈希,每个结点权值赋予一个随机数,一个连通块的哈希定义为该连通块内所有结点权值的异或和,这样方便撤销。整张图的哈希值定义为所有连通块的哈希值之和。最终时间复杂度为 \(O((m+k)\log k\log n)\)

Code

#include <bits/stdc++.h>
using namespace std;

#define LL long long

const int maxn = 100005;

struct Graph {
    struct edge { int Next, to; };
    edge G[200010];
    int head[100010];
    int cnt;

    Graph() :cnt(2) {}
    void clear(int n) {
        cnt = 2;fill(head, head + n + 2, 0);
    }
    void add_edge(int u, int v) {
        G[cnt].to = v;
        G[cnt].Next = head[u];
        head[u] = cnt++;
    }
};

struct UFS {
    int f[maxn], rk[maxn];
    pair<LL, LL> val[maxn];
    stack<pair<int, int>> opt;
    pair<LL, LL> hashval;
    int find(int u) {
        while (u ^ f[u]) u = f[u];
        return u;
    }
    void merge(int u, int v) {
        if ((u = find(u)) == (v = find(v))) return;
        if (rk[u] > rk[v]) swap(u, v);
        opt.push(make_pair(u, rk[u] == rk[v]));
        f[u] = v; rk[v] += (rk[u] == rk[v]);
        hashval.first -= val[u].first + val[v].first;
        hashval.second -= val[u].second + val[v].second;
        val[v].first ^= val[u].first;
        val[v].second ^= val[u].second;
        hashval.first += val[v].first;
        hashval.second += val[v].second;
    }
    void undo() {
        int u = opt.top().first, v = f[u];
        rk[v] -= opt.top().second;
        hashval.first -= val[v].first;
        hashval.second -= val[v].second;
        val[v].first ^= val[u].first;
        val[v].second ^= val[u].second;
        hashval.first += val[u].first + val[v].first;
        hashval.second += val[u].second + val[v].second;
        f[u] = u; opt.pop();
    }
};

UFS S;
Graph G;
map<pair<int, int>, int> mp;
pair<int, int> edges[maxn << 1];
set<pair<int, int>> s[maxn << 1];
vector <pair<int, int>> T[maxn << 2];
struct opt { int o, u, v; }opts[maxn];
int dfn[maxn], dfn_t[maxn], sz[maxn];
int t, k, n, m, dfn_index;

void DFS(int u, int fa) {
    dfn[u] = ++dfn_index;
    dfn_t[dfn[u]] = u;
    sz[u] = 1;
    for (int i = G.head[u];i;i = G.G[i].Next) {
        int v = G.G[i].to;
        if (v == fa) continue;;
        DFS(v, u);
        sz[u] += sz[v];
    }
}

void build(int rt, int L, int R) {
    T[rt].clear();
    if (L == R) return;
    int mid = (L + R) >> 1;
    build(rt << 1, L, mid);
    build(rt << 1 | 1, mid + 1, R);
}

void insert(int rt, int L, int R, int QL, int QR, pair<int, int> x) {
    if (R < QL || QR < L) return;
    if (QL <= L && R <= QR) { T[rt].push_back(x); return; }
    int mid = (L + R) >> 1;
    insert(rt << 1, L, mid, QL, QR, x);
    insert(rt << 1 | 1, mid + 1, R, QL, QR, x);
}

pair<LL, LL> hashval[maxn];
pair<LL, LL> val[maxn];

void DFS(int rt, int L, int R) {
    int sz = S.opt.size();
    pair<LL, LL> curval = S.hashval;
    for (auto x : T[rt])
        S.merge(x.first, x.second);
    if (L == R) {
        hashval[dfn_t[L]] = S.hashval;
        while (S.opt.size() != sz) S.undo();
        return;
    }
    int mid = (L + R) >> 1;
    DFS(rt << 1, L, mid);
    DFS(rt << 1 | 1, mid + 1, R);
    while (S.opt.size() != sz) S.undo();
}

vector<int> group[maxn];
map<pair<LL, LL>, int> mp2;

void get_ans() {
    int idx = 0; mp2.clear();
    for (int i = 1;i <= k;++i) {
        int id = 0;
        if (!mp2.count(hashval[i])) { mp2[hashval[i]] = ++idx; id = idx; group[idx].clear(); }
        else id = mp2[hashval[i]];
        group[id].push_back(i);
    }
    printf("%d\n", idx);
    for (int i = 1;i <= idx;++i) {
        printf("%d", group[i].size());
        for (auto x : group[i])
            printf(" %d", x);
        printf("\n");
    }
}

int main() {
    mt19937 gen(chrono::system_clock::now().time_since_epoch().count());
    uniform_int_distribution<LL> dis(1, 1e9);
    for (int i = 1;i <= 100000;++i)
        val[i] = make_pair(dis(gen), dis(gen));
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d%d", &k, &n, &m);
        mp.clear(); G.clear(k);

        dfn_index = 0;
        int index = 0;
        S.hashval = make_pair(0LL, 0LL);
        for (int i = 1;i <= n;++i) {
            S.f[i] = i;
            S.rk[i] = 0;
            S.val[i] = val[i];
            S.hashval.first += val[i].first;
            S.hashval.second += val[i].second;
        }
        while (!S.opt.empty()) S.opt.pop();
        for (int i = 1;i <= m;++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            if (u > v) swap(u, v);
            mp[make_pair(u, v)] = ++index;
            edges[index].first = u;
            edges[index].second = v;
            s[index].clear();
            s[index].insert(make_pair(1, k));
        }
        char buf[10];
        for (int i = 2;i <= k;++i) {
            int p, u, v;
            scanf("%d%s%d%d", &p, buf, &u, &v);
            if (u > v) swap(u, v);
            if (buf[0] == 'a') opts[i].o = 1;
            else opts[i].o = 0;
            opts[i].u = u; opts[i].v = v;
            G.add_edge(p, i);
            G.add_edge(i, p);
        }
        DFS(1, 0);
        for (int i = 2;i <= k;++i) {
            int u = opts[i].u, v = opts[i].v;
            int id = 0;
            if (mp.count(make_pair(u, v))) id = mp[make_pair(u, v)];
            else {
                mp[make_pair(u, v)] = id = ++index; s[index].clear();
                edges[index].first = u;
                edges[index].second = v;
            }
            if (opts[i].o) { // add
                s[id].insert(make_pair(dfn[i], dfn[i] + sz[i] - 1));
            }
            else { // remove
                auto it = s[id].lower_bound(make_pair(dfn[i], maxn)); --it;
                int l = it->first, r = it->second;
                s[id].erase(it);
                if (l <= dfn[i] - 1) s[id].insert(make_pair(l, dfn[i] - 1));
                if (dfn[i] + sz[i] <= r) s[id].insert(make_pair(dfn[i] + sz[i], r));
            }
        }
        build(1, 1, k);
        for (int i = 1;i <= index;++i)
            for (auto x : s[i])
                insert(1, 1, k, x.first, x.second, edges[i]);
        DFS(1, 1, k);
        get_ans();
    }
    return 0;
}
posted @ 2022-08-30 19:41  AE酱  阅读(39)  评论(0编辑  收藏  举报