bzoj 4998 星球联盟

新技能 get √ :LCT 维护边双连通分量

 

这题题意就是动态加边,每次求边的两端是否在一个边双连通分量里,输出 "No" 或者边双连通分量的大小

可以用两个并查集分别记录连通性和双连通性,如果还没连通就是 "No" 并在 LCT 上连边,否则直接把这条链 split 出来查即可

注意 LCT 维护的是双连通分量,所以每次跳 fa 的时候不再是 fa,而是 fa 所在的双连通分量

#include <bits/stdc++.h>
#define LL long long
#define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i)
#define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i)
using namespace std;
inline int read() {
    int x = 0,f = 1; char ch = getchar();
    for(; !isdigit(ch); ch = getchar())if(ch == '-') f = -f;
    for(; isdigit(ch); ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 200010;
int par[maxn], par2[maxn], size[maxn], ans;
inline int find(int x) {return x == par[x] ? x : par[x] = find(par[x]);}
inline int find2(int x) {return x == par2[x] ? x : par2[x] = find2(par2[x]);}
int n, m, p;
#define ls ch[x][0]
#define rs ch[x][1]
int ch[maxn][2], fa[maxn], rev[maxn], st[maxn], top;
inline int isroot(int x) { return ch[find(fa[x])][0] != x && ch[find(fa[x])][1] != x; }
inline void pushdown(int x) {
    if(rev[x]) {
        swap(ls, rs); 
        if(ls) rev[ls] ^= 1; 
        if(rs) rev[rs] ^= 1;
        rev[x] = 0;
    }
}
inline void rotate(int x) {
    int y = find(fa[x]), z = find(fa[y]);
    int l = (ch[y][1] == x), r = l ^ 1;
    if(!isroot(y)) ch[z][ch[z][1] == y] = x;
    fa[x] = z; fa[ch[x][r]] = y; fa[y] = x;
    ch[y][l] = ch[x][r]; ch[x][r] = y;
}
inline void splay(int x) {
    st[top = 1] = x;
    for(int i=x;!isroot(i);i=find(fa[i])) st[++top] = find(fa[i]);
    for(int i=top;i;i--) pushdown(st[i]);
    //PUSHDOWN(x);
    //cout << x << endl;
    while(!isroot(x)) {
        int y = find(fa[x]), z = find(fa[y]);
        if(!isroot(y)) {
            if(ch[z][0] == y ^ ch[y][0] == x) rotate(x);
            else rotate(y);
        } rotate(x);
    }
}
inline void access(int x) {
    for(int y = 0; x; splay(x), rs = y, y = x, x = find(fa[x]));
}
inline void makeroot(int x) {
    access(x); splay(x); rev[x] ^= 1;
}
inline void link(int x, int y) {
    makeroot(x); fa[x] = y;
}
void dfs(int x, int pre) {
    if(!x) return;
    ans += size[x];
    if(x != pre) size[pre] += size[x], par[x] = pre;
    dfs(ls, pre); dfs(rs, pre);
}
inline void lnk(int u, int v) {
    ans = 0;
    if(find2(u) != find2(v)) {
        par2[par2[u]] = par2[v];
        //cout << u << " " << v << endl;
        link(u, v);
    }
    else {
        makeroot(u); access(v); splay(v); dfs(v, v);
    }
}
int main() {
    n = read(), m = read(), p = read();
    rep(i, 1, n) size[i] = 1, par[i] = i, par2[i] = i;
    rep(i, 1, m) {
        int u = find(read()), v = find(read());
        lnk(u, v);
    }
    rep(i, 1, p) {
        int u = find(read()), v = find(read()); lnk(u, v);
        printf(ans ? "%d\n" : "No\n", ans);
    }
}
View Code

 

转载于:https://www.cnblogs.com/Kong-Ruo/p/10634512.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值