关于用LCT维护连通性的一点想法

3 篇文章 0 订阅
2 篇文章 0 订阅

昨天刚学了LCT,做了几道关于用LCT维护连通性的的题目,分享一下自己一点拙见。

BZOJ2049 洞穴勘测
题目大意:给定一棵树,开始时树上没有边,每次操作可以在两点之间删除或添加一条边,查询两点间是否联通。
分析:因为LCT可以提供删边和加边的操作,每次查询我们只要判断两点是否在同一颗Splay中即可。
代码:

#include<iostream>
#include<cstdio>
using namespace std;
int n,m,x,y,f[10010],ch[10010][2],size[100010],res[10010];
char s[10];
void update(int x)
{
    size[x]=1+size[ch[x][0]]+size[ch[x][1]];
}
void pushdown(int x)
{
    if(res[x]&&x)
    {
        if(ch[x][0]) res[ch[x][0]]^=1;
        if(ch[x][1]) res[ch[x][1]]^=1;
        swap(ch[x][0],ch[x][1]);
        res[x]=0;
    }
}
int is_root(int x)
{
    return (ch[f[x]][0]!=x&&ch[f[x]][1]!=x);
}
int get_son(int x)
{
    return ch[f[x]][1]==x;
}
void rotate(int x)
{
    int old=f[x],oldf=f[old],k=get_son(x);
    if(!is_root(old)) ch[oldf][ch[oldf][1]==old]=x;
    ch[old][k]=ch[x][k^1];
    f[ch[old][k]]=old;
    ch[x][k^1]=old;
    f[old]=x;
    f[x]=oldf;
    update(old);
    update(x);
}
void push(int x)
{
    if(!is_root(x)) push(f[x]);
    pushdown(x);
}
void splay(int x)
{
    push(x);
    for(int fa;!is_root(x);rotate(x))
        if(!is_root(fa=f[x]))
            rotate(get_son(x)==get_son(fa)?fa:x);
}
void access(int x)
{
    for(int y=0;x;y=x,x=f[x])
    {
        splay(x);
        ch[x][1]=y;
    }
}
int find_root(int x)
{
    access(x);
    splay(x);
    while(ch[x][0])
        x=ch[x][0];
    return x;
}
void make_root(int x)
{
    access(x);
    splay(x);
    res[x]^=1;
}
void link(int x,int y)
{
    make_root(x);
    f[x]=y;
    splay(x);
}
void cut(int x,int y)
{
    make_root(x);
    access(y);
    splay(y);
    ch[y][0]=f[x]=0;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",s);
        scanf("%d%d",&x,&y);
        if (s[0]=='Q')
        {
            if (find_root(x)==find_root(y))
                printf("Yes\n");
            else
                printf("No\n");
        }
        else if (s[0]=='C')
            link(x,y);
        else if (s[0]=='D')
            cut(x,y);
    }
}

BZOJ 2959 长跑
题目大意:依次加入边,修改点权,询问两点间走一条路径的最大点权和。
分析:用LCT维护双联通分量。用一个并查集维护连通性,另一个维护点双的编号,splay维护一个点双。
代码:

#include<iostream>  
#include<cstdio>  
#include<algorithm>  
#include<cmath>  
#include<cstring>  
#define N 150003  
using namespace std;  
int n,m,p,x,y,top;
int father[N],res[N],a[N],st[N];  
int belong[N],f[N],ch[N][2],val[N],maxn[N]; 
int findset(int x)
{
    if(belong[x]!=x) belong[x]=findset(belong[x]);
    return belong[x];
}
int find(int x)
{
    if(father[x]!=x) father[x]=find(father[x]);
    return father[x];
}
int is_root(int x)
{
    int fa=findset(f[x]);
    return ch[fa][0]!=x&&ch[fa][1]!=x;
}
int get_son(int x)
{
    return ch[findset(f[x])][1]==x;
}
void update(int x)
{
    maxn[x]=val[x];
    if(ch[x][0]) maxn[x]+=maxn[ch[x][0]];
    if(ch[x][1]) maxn[x]+=maxn[ch[x][1]];
}
void pushdown(int x)
{
    if(x&&res[x])
    {
        swap(ch[x][0],ch[x][1]);
        if(ch[x][0]) res[ch[x][0]]^=1;
        if(ch[x][1]) res[ch[x][1]]^=1;
        res[x]=0;
    }
}
void rotate(int x)
{
    int old=f[x],oldf=f[old],k=get_son(x);
    if(!is_root(old)) ch[oldf][ch[oldf][1]==old]=x;
    ch[old][k]=ch[x][k^1];
    f[ch[old][k]]=old;
    ch[x][k^1]=old;
    f[old]=x;
    f[x]=oldf;
    update(old);
    update(x);
}
void splay(int x)  
{  
    top=0;st[++top]=x;  
    for(int i=x;!is_root(i);i=findset(f[i]))  
        st[++top]=findset(f[i]);  
    for(int i=top;i>=1;i--) pushdown(st[i]),f[st[i]]=findset(f[st[i]]);  
    int y;  
    while(!is_root(x))
    {  
        y=f[x];  
        if(!is_root(y)) 
            rotate(get_son(x)==get_son(y)?y:x);  
        rotate(x);  
    }  
}  
void access(int x)
{
    for(int y=0;x;y=x,x=findset(f[x]))
    {
        splay(x);
        ch[x][1]=y;
        update(x);
    }
}
void makeroot(int x)
{
    access(x);
    splay(x);
    res[x]^=1;
}
void link(int x,int y)
{
    makeroot(x);
    f[x]=y;
    splay(x);
}
void merge(int x,int y)
{
    belong[findset(x)]=findset(y);
    pushdown(x);
    if(x!=y) val[y]+=val[x];
    if(ch[x][0]) merge(ch[x][0],y);
    if(ch[x][1]) merge(ch[x][1],y);
    ch[x][1]=ch[x][0]=0;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&val[i]);
        a[i]=val[i];
        father[i]=i;
        belong[i]=i;
    } 
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&p,&x,&y);
        if(p==1)
        {
            x=findset(x),y=findset(y);
            if(x==y) continue;
            int r1=find(x),r2=find(y);
            if(r1!=r2)
            {
                father[r2]=r1;
                link(x,y);
            }
            else
            {
                makeroot(x);
                access(y);
                splay(y);
                merge(y,y);
                update(y);
            }
        }
        else if(p==2)
        {
            int xx=x;
            x=findset(x);
            access(x);
            splay(x);
            val[x]+=y-a[xx];
            a[xx]=y;
            update(x);
        }
        else if(p==3)
        {
            x=findset(x),y=findset(y);
            if(find(x)!=find(y)) printf("-1\n");
            else
            {
                makeroot(x);
                access(y);
                splay(y);
                printf("%d\n",maxn[y]);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值