BZOJ 3673 / 3674 可持久化并查集 by zky [主席树]

本文介绍了一种使用主席树优化非路径压缩并查集的方法,通过启发式合并确保树深度不超过logn,实现了O(mlognlogn)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给你三种操作

1 a b 合并a,b所在集合

2 k 回到第k次操作之后的状态(查询算作操作)

3 a b 询问a,b是否属于同一集合,是则输出1否则输出0

题解:通过主席树维护非路径压缩的并查集,并查集通过启发式合并保证树深度不超过logn,总复杂度O(mlognlogn)。

AC代码:

#include<stdio.h>  
#include<algorithm>  
#define N 400005  
using namespace std;  
int tree[N*23],lchild[N*23],rchild[N*23],root[N],Rank[N*23];  
int n,m,tot;  
void build(int L,int R,int root)  
{  
    if(L==R)  
    {  
        tree[root]=L;  
        return ;  
    }  
    int mid=L+R>>1;  
    build(L,mid,lchild[root]=++tot);  
    build(mid+1,R,rchild[root]=++tot);  
}  
void update(int last,int cur,int L,int R,int pos,int k,int flag)  
{  
    tree[cur]=tree[last];  
    Rank[cur]=Rank[last];  
    lchild[cur]=lchild[last];  
    rchild[cur]=rchild[last];  
    if(L==R)  
    {  
        if(flag==0)tree[cur]=k;  
        Rank[cur]+=flag;  
        return ;  
    }  
    int mid=L+R>>1;  
    if(pos<=mid)update(lchild[last],lchild[cur]=++tot,L,mid,pos,k,flag);  
    else update(rchild[last],rchild[cur]=++tot,mid+1,R,pos,k,flag);  
}  
int query(int x,int L,int R,int root)  
{  
    if(L==R)return root;  
    int mid=L+R>>1;  
    if(x<=mid)return query(x,L,mid,lchild[root]);  
    else return query(x,mid+1,R,rchild[root]);  
}  
int find(int x,int root)  
{  
    int fa=query(x,1,n,root);  
    if(tree[fa]!=x)return find(tree[fa],root);  
    return fa;  
}  
int main()  
{  
    scanf("%d%d",&n,&m);  
    build(1,n,root[0]);  
    int last=0;  
    for(int i=1;i<=m;i++)  
    {  
        int op;  
        scanf("%d",&op);  
        if(op==1)  
        {  
            int u,v;  
            scanf("%d%d",&u,&v);  
            u^=last;v^=last;  
            root[i]=root[i-1];  
            u=find(u,root[i-1]);  
            v=find(v,root[i-1]);  
            if(tree[u]==tree[v])continue;  
            if(Rank[u]<Rank[v])swap(u,v);  
            update(root[i-1],root[i]=++tot,1,n,tree[v],tree[u],0); 
            if(Rank[u]==Rank[v])
			{
				int now=++tot;
				update(root[i],now,1,n,tree[u],tree[u],1); 
				root[i]=now;  
			}
        }  
        if(op==2)  
        {  
            int k;  
            scanf("%d",&k);  
            k^=last;  
            root[i]=root[k];  
        }  
        if(op==3)  
        {  
               
            int u,v;  
            scanf("%d%d",&u,&v);  
            u^=last;v^=last;  
            root[i]=root[i-1];  
            u=find(u,root[i-1]);  
            v=find(v,root[i-1]);  
            printf("%d\n",tree[u]==tree[v]);  
            last=tree[u]==tree[v];  
        }  
    }   
}  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值