LCT
首先要知道一条虚边连接了
动态树(LCT)
操作
如果一个点不是根,则考虑它既不是父亲的左儿子也不是父亲的右儿子。代码:
bool isroot(int x){
return tr[tr[x].p].s[0]!=x&&tr[tr[x].p].s[1]!=x;
}
操作
假设
接下来发现父亲少了一个儿子,于是把自己的另外一边的儿子给父亲,同时把他连接自己的边断掉。
最后我们只需要链接
代码:
void rotate(int x){
int y=tr[x].p,z=tr[y].p;
int k=tr[y].s[1]==x;
if(!isroot(y))tr[z].s[tr[z].s[1]==y]=x;
tr[x].p=z;
tr[y].s[k]=tr[x].s[k^1];
tr[tr[x].s[k^1]].p=y;
tr[x].s[k^1]=y;
tr[y].p=x;
pushup(y);
pushup(x);
}
操作
先分类讨论一下,看
不难发现,如果在一条直线上,则先转
但是在进行旋转之前,我们要先把转上去的路径做个
void splay(int x){
int top=0,r=x;
stk[++top]=r;
while(!isroot(r))stk[++top]=r=tr[r].p;
while(top)pushdown(stk[top--]);
while(!isroot(x)){
int y=tr[x].p,z=tr[y].p;
if(!isroot(y)){
if((tr[y].s[1]==x)^(tr[z].s[1]==y))rotate(x);
else rotate(y);
}
rotate(x);
}
}
操作
我们需要把根节点到
void access(int x){
int z=x;
for(int y=0;x;y=x,x=tr[x].p){
splay(x);
tr[x].s[1]=y;
pushup(x);
}
}
操作
把
void makeroot(int x){
access(x);
splay(x);
pushrev(x);
}
找一个点的根其实就是,先把
int findroot(int x){
access(x);
splay(x);
while(tr[x].s[0]){
pushdown(x);
x=tr[x].s[0];
}
splay(x);
return x;
}
函数
下面认为两件套指的是先打通
我们要拉出一条
void split(int x,int y){
makeroot(x);
access(y);
splay(y);
}
函数
首先连完边要保证是一棵树。于是先把
void link(int x,int y){
makeroot(x);
if(findroot(y)!=x)tr[x].p=y;
}
断边直接把
void cut(int x,int y){
split(x,y);
if(tr[y].s[0]==x&&!tr[x].s[1])tr[x].p=tr[y].s[0]=0;
}
完整代码:
#include<bits/stdc++.h>
#define int long long
#define N 100005
using namespace std;
int n,m,stk[N];
struct node{
int s[2],p,v,sum,rev;
}tr[N];
void pushrev(int x){
swap(tr[x].s[0],tr[x].s[1]);
tr[x].rev^=1;
}
void pushup(int x){
tr[x].sum=tr[tr[x].s[0]].sum^tr[tr[x].s[1]].sum^tr[x].v;
}
void pushdown(int x){
if(tr[x].rev){
pushrev(tr[x].s[0]);
pushrev(tr[x].s[1]);
tr[x].rev=0;
}
}
bool isroot(int x){
return tr[tr[x].p].s[0]!=x&&tr[tr[x].p].s[1]!=x;
}
void rotate(int x){
int y=tr[x].p,z=tr[y].p;
int k=tr[y].s[1]==x;
if(!isroot(y))tr[z].s[tr[z].s[1]==y]=x;
tr[x].p=z;
tr[y].s[k]=tr[x].s[k^1];
tr[tr[x].s[k^1]].p=y;
tr[x].s[k^1]=y;
tr[y].p=x;
pushup(y);
pushup(x);
}
void splay(int x){
int top=0,r=x;
stk[++top]=r;
while(!isroot(r))stk[++top]=r=tr[r].p;
while(top)pushdown(stk[top--]);
while(!isroot(x)){
int y=tr[x].p,z=tr[y].p;
if(!isroot(y)){
if((tr[y].s[1]==x)^(tr[z].s[1]==y))rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
int z=x;
for(int y=0;x;y=x,x=tr[x].p){
splay(x);
tr[x].s[1]=y;
pushup(x);
}
}
void makeroot(int x){
access(x);
splay(x);
pushrev(x);
}
int findroot(int x){
access(x);
splay(x);
while(tr[x].s[0]){
pushdown(x);
x=tr[x].s[0];
}
splay(x);
return x;
}
void split(int x,int y){
makeroot(x);
access(y);
splay(y);
}
void link(int x,int y){
makeroot(x);
if(findroot(y)!=x)tr[x].p=y;
}
void cut(int x,int y){
split(x,y);
if(tr[y].s[0]==x&&!tr[x].s[1])tr[x].p=tr[y].s[0]=0;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>tr[i].v;
}
while(m--){
int t,x,y;
cin>>t>>x>>y;
if(!t){
split(x,y);
cout<<tr[y].sum<<'\n';
}
else if(t==1)link(x,y);
else if(t==2)cut(x,y);
else{
splay(x);
tr[x].v=y;
pushup(x);
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】