并查集重学

并查集重学

好多高级的操作,发现我学的那个并查集就是垃圾并查集,看点高级东西

可撤销并查集

这个就不能路径压缩了,只能按秩合并

撤销操作就是用一个栈记录所有的合并,撤销的时候全倒出来就好了

注意时间顺序,保证不了就用线段树分治(就是把连边的时间区域覆盖在线段树上,然后在线段树上\(dfs\))

code
struct D{
    int fa[N],siz[N],top;
    pair<int,int> sta[N];
    void init(int n){fo(i,1,n)fa[i]=i,siz[i]=1;}
    int find(int x){return fa[x]==x?x:find(fa[x]);}
    int merge(int x,int y){
        int fx=find(x),fy=find(y);
        if(fx==fy)return 0;
        if(siz[fx]>siz[fy])swap(fx,fy);
        fa[fx]=fy;siz[fy]+=siz[fx];
        sta[++top]=make_pair(fx,fy);
        return 1;
    }
    void cancel(){
        fa[sta[top].first]=sta[top].first;
        siz[sta[top].second]-=siz[sta[top].first];
        top--;return ;
    }
}d[2];

例题

Luogu4121 WC2005 双面棋盘

这个就是线段树分治,然后,直接两个并查集一起跑就好了

code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*t;
}
const int N=40005;
struct D{
    int fa[N],siz[N],top;
    pair<int,int> sta[N];
    void init(int n){fo(i,1,n)fa[i]=i,siz[i]=1;}
    int find(int x){return fa[x]==x?x:find(fa[x]);}
    int merge(int x,int y){
        int fx=find(x),fy=find(y);
        if(fx==fy)return 0;
        if(siz[fx]>siz[fy])swap(fx,fy);
        fa[fx]=fy;siz[fy]+=siz[fx];
        sta[++top]=make_pair(fx,fy);
        return 1;
    }
    void cancel(){
        fa[sta[top].first]=sta[top].first;
        siz[sta[top].second]-=siz[sta[top].first];
        top--;return ;
    }
}d[2];
int n,m,jz[205][205],s[N];
pair<int,int> ans[N];
bool pd(int x,int y){return x<=n&&x>0&&y<=n&&y>0;}
int id(int x,int y){return (x-1)*n+y;}
struct X{
    #define ls x<<1
    #define rs x<<1|1
    vector<pair<int,int> > vec[2][N*4];
    void ins(int x,int l,int r,int ql,int qr,pair<int,int> v,int t){
        if(ql>qr)return ;
        if(ql<=l&&r<=qr){
            vec[t][x].push_back(v);
            return ;
        }
        int mid=l+r>>1;
        if(ql<=mid)ins(ls,l,mid,ql,qr,v,t);
        if(qr>mid)ins(rs,mid+1,r,ql,qr,v,t);
        return ;
    }
    void dfs(int x,int l,int r,int s0,int s1){
        int n0=0,n1=0;
        for(pair<int,int> i:vec[0][x])n0+=d[0].merge(i.first,i.second);
        for(pair<int,int> i:vec[1][x])n1+=d[1].merge(i.first,i.second);
        s0-=n0;s1-=n1;
        if(l==r){
            // ans[l]=make_pair(s0,s1);
            ans[l]=make_pair(s0-s[l],s1-(n*n-s[l]));
            while(n0--)d[0].cancel();
            while(n1--)d[1].cancel();
            return ;
        }
        int mid=l+r>>1;
        dfs(ls,l,mid,s0,s1);
        dfs(rs,mid+1,r,s0,s1);
        while(n0--)d[0].cancel();
        while(n1--)d[1].cancel();
        return ;
    }
    #undef ls
    #undef rs
}x;
map<pair<int,int>,pair<int,int>> mp;
void work(int a,int b,int c,int d,int i){
    if(!pd(a,b)||!pd(c,d))return ;
    if(jz[a][b]!=jz[c][d]){
        x.ins(1,0,m,mp[make_pair(id(a,b),id(c,d))].first,i-1,make_pair(id(a,b),id(c,d)),mp[make_pair(id(a,b),id(c,d))].second);
        mp.erase(make_pair(id(a,b),id(c,d)));
    }
    if(jz[a][b]==jz[c][d]){
        mp.insert(make_pair(make_pair(id(a,b),id(c,d)),make_pair(i,jz[a][b])));
    }
}
signed main(){
    n=read();
    fo(i,1,n)fo(j,1,n)jz[i][j]=read(),jz[i][j]?s[0]++:s[0];
    fo(i,1,n)fo(j,1,n){
        if(pd(i+1,j)&&jz[i][j]==jz[i+1][j])mp.insert(make_pair(make_pair(id(i,j),id(i+1,j)),make_pair(0,jz[i][j])));
        if(pd(i,j+1)&&jz[i][j]==jz[i][j+1])mp.insert(make_pair(make_pair(id(i,j),id(i,j+1)),make_pair(0,jz[i][j])));
    }
    m=read();
    fo(i,1,m){
        int x=read(),y=read();jz[x][y]^=1;
        work(x-1,y,x,y,i);
        work(x,y-1,x,y,i);
        work(x,y,x+1,y,i);
        work(x,y,x,y+1,i);
        s[i]=s[i-1];
        jz[x][y]?s[i]++:s[i]--;
    }
    for(pair<pair<int,int>,pair<int,int> > i:mp)x.ins(1,0,m,i.second.first,m,i.first,i.second.second);
    d[0].init(n*n);d[1].init(n*n);x.dfs(1,0,m,n*n,n*n);
    fo(i,1,m)printf("%d %d\n",ans[i].second,ans[i].first);
}

考试题

类似的题,不过这个比上一个更加简单

可持久化并查集

可持久化数组+并查集

众所周知,主席树又名可持久化数组

于是我们用主席树维护每一个版本的\(fa\)\(dep\)

按秩合并,复杂度\(\mathcal{O(nlog^2n)}\)

Luogu3402 模板题

code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*t;
}
const int N=1e5+5;
int n,m;
int rt[N];
struct Z{
    struct POT{
        int fa,dep,ls,rs;
    }tr[N*62];
    int seg;
    void build(int &x,int l,int r){
        x=++seg;
        if(l==r)return tr[x].fa=l,tr[x].dep=1,void();
        int mid=l+r>>1;
        build(tr[x].ls,l,mid);
        build(tr[x].rs,mid+1,r);
    }
    void ins(int pr,int &x,int l,int r,int pos,int f,int d){
        x=++seg;tr[x]=tr[pr];
        if(l==r)return tr[x].fa=f,tr[x].dep=d,void();
        int mid=l+r>>1;
        if(pos<=mid)ins(tr[pr].ls,tr[x].ls,l,mid,pos,f,d);
        else ins(tr[pr].rs,tr[x].rs,mid+1,r,pos,f,d);
    }
    int query(int x,int l,int r,int pos){
        if(l==r)return x;
        int mid=l+r>>1;
        if(pos<=mid)return query(tr[x].ls,l,mid,pos);
        else return query(tr[x].rs,mid+1,r,pos);
    }
}z;
int find(int rt,int x){
    int now=z.query(rt,1,n,x);
    if(z.tr[now].fa==x)return x;
    else return find(rt,z.tr[now].fa);
}
int cur;
signed main(){
    n=read();m=read();
    z.build(rt[0],1,n);
    fo(q,1,m){
        int tp=read(),a=read(),b;
        if(tp==1){
            b=read();int fx=find(rt[q-1],a),fy=find(rt[q-1],b);
            if(fx==fy)rt[q]=rt[q-1];
            else {
                int nx=z.query(rt[q-1],1,n,fx),ny=z.query(rt[q-1],1,n,fy);
                if(z.tr[nx].dep>z.tr[ny].dep)swap(nx,ny),swap(fx,fy);
                if(z.tr[nx].dep==z.tr[ny].dep){
                    z.ins(rt[q-1],rt[q],1,n,fx,fy,z.tr[nx].dep);
                    z.ins(rt[q],rt[q],1,n,fy,fy,z.tr[ny].dep+1);
                }
                else z.ins(rt[q-1],rt[q],1,n,fx,fy,z.tr[nx].dep);
            }
        }
        if(tp==2)rt[q]=rt[a];
        if(tp==3){
            b=read();rt[q]=rt[q-1];
            int fx=find(rt[q],a),fy=find(rt[q],b);
            if(fx==fy)printf("1\n");
            else printf("0\n");
        }
    }
}

例题

Luogu4768 NOI2018 归程

这个题就是可以单源最短路+可持久化并查集,查找最近的就行了

代码没写,咕了......

奇奇怪怪零零散散的并查集小清新

CF891C Envy

神奇的\(kruskal\),神奇的性质:

不同权值之间互不影响,除非是权值大的边连接了一个所有边权值都比他小的连通块(也就是永远不能加进去的情况)

1、某一权值的边的数量一定(在任意合法的最小生成树中)

某一条边如果可以被替换,那么一定满足两条边权值相等,那么一换一,数量不变

2、将小于某一值的边全部加入到生成树中,生成树的形态一定,连通性一定

两条边可以互相替换,首先权值一定相等,其次链接的联通块是同一个,那么连通性不变

于是这个题就可以利用这个性质

我们将权值相等的一起处理,因为权值不相等的互不影响,由上面性质可得

由上面性质可得,当前权值加进去之前,连通性一定,也就是说,当前权值的边连起来只要不会构成环,那么一定可以在同一颗生成树中

当然,如果这个权值连接的两点被权值比它小的边连接的话,那就永远不能在生成树里了

于是我们预处理每一条边连接的两个连通块,直接判断就行了

code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*t;
}
const int N=5e5+5;
int n,m,q,qus[N];
int fai[N];
void init(int n){fo(i,1,n)fai[i]=i;}
int find(int x){return fai[x]==x?x:fai[x]=find(fai[x]);}
struct E{int x,y,v,id;}e[N],d[N];
bool com(E a,E b){return a.v<b.v;}
bool come(E a,E b){return a.id<b.id;}
bool comp(int x,int y){return e[x].v<e[y].v;}
int sta[N],top;
signed main(){
    n=read();m=read();
    fo(i,1,m)e[i].x=read(),e[i].y=read(),e[i].v=read(),e[i].id=i;
    sort(e+1,e+m+1,com);init(n);
    fo(i,1,m){
        if(e[i].v!=e[i-1].v){
            int j=i;
            do{
                d[e[j].id].x=find(e[j].x);
                d[e[j].id].y=find(e[j].y);
                j++;
            }while(e[j-1].v==e[j].v&&j<=m);
        }
        int fx=find(e[i].x),fy=find(e[i].y);
        if(fx==fy)continue;
        if(fx<fy)swap(fx,fy);
        fai[fx]=fy;
    }
    sort(e+1,e+m+1,come);
    q=read();init(n);
    while(q--){
        int k=read();bool flag=true;
        fo(i,1,k)qus[i]=read();
        sort(qus+1,qus+k+1,comp);
        fo(i,1,k){
            int j=i;
            do{
                int fx=find(d[qus[j]].x),fy=find(d[qus[j]].y);
                if(fx==fy){flag=false;break;}
                if(fx<fy)swap(fx,fy);
                sta[++top]=fx;fai[fx]=fy;j++;
            }while(e[qus[j-1]].v==e[qus[j]].v&&j<=k);
            while(top)fai[sta[top]]=sta[top--];
            if(!flag)break;i=j-1;
        }
        if(flag)printf("YES\n");
        else printf("NO\n");
    }
}
posted @ 2021-12-16 09:47  fengwu2005  阅读(44)  评论(0编辑  收藏  举报