可撤销并查集

可撤销的思路就是,我们用一个栈记录每次的合并操作,被合并的那个根节点 x x x,然后我们每次合并都是 f [ x ] = y , s z [ y ] + = s z [ x ] f[x]=y,sz[y]+=sz[x] f[x]=y,sz[y]+=sz[x],那么执行这个操作之前, f [ x ] = x , s z [ y ] = s z [ y ] − s z [ x ] f[x]=x,sz[y]=sz[y]-sz[x] f[x]=x,sz[y]=sz[y]sz[x],那么我们知道 x x x,就可以执行撤销操作,撤销这次合并了,这里 y = f [ x ] y=f[x] y=f[x],不用保存 y y y

然后撤销到哪一步,需要我们制定,这可以在执行前,记录栈大小,那么撤销时再次撤销到这个深度就行了是,所以撤销函数需要传一个参数,表示要撤销到的栈大小

struct DSU{
    int n = 0, tot = 0, fa[N], sz[N], s[N];
    void ins(){n++, fa[n] = n, sz[n] = 1;}//插入节点
    int F(int x){return fa[x] == x? x : F(fa[x]);}//即find查找函数
    void U(int x, int y){//合并函数
        x = F(x), y = F(y);
        if(x == y) return;
        if(sz[x] < sz[y]) swap(x, y);
        s[++tot] = y, fa[y] = x, sz[x] += sz[y];
    }
    void D(){//删除栈顶边
        if(!tot) return;
        int y = s[tot--]; sz[fa[y]] -= sz[y], fa[y] = y;
    }
    void back(int t = 0){while(tot > t) D();}//删除到只剩t条边
}d;

你可能感兴趣的:(算法)