「UVA 11987」Almost Union-Find 「带权并查集」「思维」

你发现,这个第二个操作不可能用普通并查集来搞,很棘手

但是你考虑一下,并查集维护的是个森林结构,并且路径压缩的时候每个森林的根是不会变的,

也就是意味着每删掉一个点你需要让他的踪影消失匿迹即可,并不需要让他在原有的树结构上消失。

具体怎么消失?把贡献全在根上减掉即可,再新建一个新点连进去。

这个新点可以用id数组表示,即id[x]为x节点现在的编号。

#include <bits/stdc++.h>

#define test(...) fprintf(stderr, __VA_ARGS__)
#define dbg(x) cerr << #x << " = " << x << '\n'

using namespace std;

typedef long long ll;
typedef pair <int, int> pii;
typedef vector <int> vi;
typedef unsigned int ui;
typedef vector <pair <int, int> > edges;

const int N = 100010;
int n, m, id[N], par[N], tot, cnt[N];
ll sum[N];
int find_par(int x) {
    return x == par[x] ? par[x] : par[x] = find_par(par[x]); 
}
void solve() {
    for (int i = 1; i <= n; ++i) 
        id[i] = i, par[i] = i, sum[i] = i, cnt[i] = 1; 
    tot = n;
    while (m--) {
        int op, p, q;
        scanf ("%d%d", &op, &p);
        if (op == 1) {
            scanf ("%d", &q);
            int rp = id[p], rq = id[q];
            rp = find_par(rp);
            rq = find_par(rq);
            if (rp == rq) continue;
            par[rp] = rq;
            sum[rq] += sum[rp];
            cnt[rq] += cnt[rp];
        } else if (op == 2) {
            scanf ("%d", &q);
            int rp = id[p], rq = id[q];
            rp = find_par(rp);
            rq = find_par(rq);
            if (rp == rq) continue;
            cnt[rp]--;
            sum[rp] -= p;
            sum[rq] += p;
            cnt[rq]++;
            id[p] = ++tot;
            par[id[p]] = rq;
        } else if (op == 3) {
            int pr = find_par(id[p]);
            printf("%d %lld\n", cnt[pr], sum[pr]);
        }
    }
}

int main() {
#ifdef LOCAL
    freopen("sample.in", "r", stdin);
#endif
    while (~scanf("%d%d", &n, &m)) solve();
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/LiM-817/p/12337442.html