Luogu P3690 【模板】Link Cut Tree (动态树)(LCT模板)

本文详细介绍了一种处理动态树结构的算法实现,通过Splay树处理动态连接、删除、修改节点值及路径异或和的操作,适用于解决复杂的动态树问题。

题目背景

动态树

题目描述

给定n个点以及每个点的权值,要你处理接下来的m个操作。操作有4种。操作从0到3编号。点从1到n编号。

0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和。保证x到y是联通的。

1:后接两个整数(x,y),代表连接x到y,若x到y已经联通则无需连接。

2:后接两个整数(x,y),代表删除边(x,y),不保证边(x,y)存在。

3:后接两个整数(x,y),代表将点x上的权值变成y。

输入输出格式

输入格式:

 

第1行两个整数,分别为n和m,代表点数和操作数。

第2行到第n+1行,每行一个整数,整数在[1,10^9]内,代表每个点的权值。

第n+2行到第n+m+1行,每行三个整数,分别代表操作类型和操作所需的量。

 

输出格式:

 

对于每一个0号操作,你须输出x到y的路径上点权的xor和。

 

输入输出样例

输入样例#1: 复制

3 3 
1
2
3
1 1 2
0 1 2 
0 1 1

输出样例#1: 复制

3
1
#include<bits/stdc++.h>
using namespace std;
int read(){
    char ch=getchar();
    int h=0;
    while(ch>'9'||ch<'0')ch=getchar();
    while(ch>='0'&&ch<='9'){h=h*10+ch-'0';ch=getchar();}
    return h;
}
const int MAXN=300001;
int N,M,zhan[MAXN],top=0;
struct Splay{int val,sum,rev,ch[2],fa;}t[MAXN];

void pushup(int x){  //向上维护异或和
    t[x].sum=t[t[x].ch[0]].sum^t[t[x].ch[1]].sum^t[x].val;//异或和
}
void reverse(int x){
    swap(t[x].ch[0],t[x].ch[1]);
    t[x].rev^=1;//标记表示已经翻转了该点的左右儿子
}
void pushdown(int x){//向下传递翻转标记
    if(!t[x].rev)return;
    if(t[x].ch[0])reverse(t[x].ch[0]);
    if(t[x].ch[1])reverse(t[x].ch[1]);
    t[x].rev=0;
}
bool isroot(int x){
    return t[t[x].fa].ch[0]!=x&&t[t[x].fa].ch[1]!=x;
}
void rotate(int x){  //Splay向上操作
    int y=t[x].fa,z=t[y].fa;
    int k=t[y].ch[1]==x;
    if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;//Attention if()
    t[x].fa=z;//注意了
    t[y].ch[k]=t[x].ch[k^1];t[t[x].ch[k^1]].fa=y;
    t[x].ch[k^1]=y;t[y].fa=x;
    pushup(y);
}
void splay(int x){  //把x弄到根
    zhan[++top]=x;
    for(int pos=x;!isroot(pos);pos=t[pos].fa)zhan[++top]=t[pos].fa;
    while(top)pushdown(zhan[top--]);
    while(!isroot(x))
    {
        int y=t[x].fa,z=t[y].fa;
        if(!isroot(y)) (t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
        rotate(x);
    }
    pushup(x);
}
void Access(int x){
    for(int y=0;x;y=x,x=t[x].fa){splay(x);t[x].ch[1]=y;pushup(x);}
}
void makeroot(int x){//函数功能:把x拎成原图的根
    Access(x);splay(x);//把x和根先弄到一起
    reverse(x);//然后打区间翻转标记,应该在根的地方打但是找不到根所以要splay(x)
}
int findroot(int x){//函数功能:找到x所在联通块的splay的根
    Access(x);splay(x);
    while(t[x].ch[0])x=t[x].ch[0];
    return x;
}
void split(int x,int y){//函数功能:把x到y的路径抠出来
    makeroot(x);//先把x弄成原图的根
    Access(y);//再把y和根的路径弄成重链
    splay(y);//那么就是y及其左子树存储的信息了
}
void link(int x,int y){//函数功能:连接x,y所在的两个联通块
    makeroot(x);//把x弄成其联通块的根
    t[x].fa=y;//连到y上(虚边)
}
void cut(int x,int y){//函数功能:割断x,y所在的两个联通块
    split(x,y);
    if (t[x].fa == y && !t[x].ch[1]){
        t[y].ch[0]=t[x].fa=0;
        pushup(y);
    }
}
int main()
{
    N=read();M=read();
    for(int i=1;i<=N;i++)
        t[i].val=read();
    for(int i=1;i<=M;i++)
    {
        int op=read(),x=read(),y=read();
        if(op==0){
            split(x,y);//抠出路径
            printf("%d\n",t[y].sum);
        }
        if(op==1){
            if(findroot(x)^findroot(y))
                link(x,y);//x,y不在同一联通块里
        }
        if(op==2)        {
            if (!findroot(x)^findroot(y))
                cut(x,y);//x,y在同一联通块
        }
        if(op==3) {//把x点的权值改成y    
            Access(x);//把x到根的路径设置为重链
            splay(x);//把x弄到该链的根结点
            t[x].val=y;
            pushup(x);//直接改x的val并更新
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值