BZOJ 3282: Tree (LCT)题解

Time Limit: 30 Sec Memory Limit: 512 MB
Submit: 2119 Solved: 973


Description

给定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。


Input

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

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

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


Output

对于每一个0号操作,你须输出X到Y的路径上点权的Xor和。


Sample Input

3 3

1

2

3

1 1 2

0 1 2

0 1 1


Sample Output

3

1


HINT

1<=N,M<=300000


Source

动态树


裸LCT,如果不会LCT的朋友可以以这道题为LCT第一题,因为非常的裸,还可以看我的LCT讲解


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
const int MAXN=300010;
using namespace std;
struct Tree{
    int child[2],sum,val,fa,rev;
}tree[MAXN];
int n,Q,opt,xx,yy;
void pushup(int x){tree[x].sum=tree[tree[x].child[0]].sum^tree[tree[x].child[1]].sum^tree[x].val;}
void pushdown(int x){
    if(tree[x].rev){
        tree[x].rev^=1;
        tree[tree[x].child[0]].rev^=1;
        tree[tree[x].child[1]].rev^=1;
        swap(tree[x].child[0],tree[x].child[1]);
    }
}
bool isroot(int x){
    return (tree[tree[x].fa].child[0]!=x)&&(tree[tree[x].fa].child[1]!=x);
}
void Pushdown(int x){
    if(!isroot(x)) Pushdown(tree[x].fa);
    pushdown(x);
}
void rotate(int x){
    int y=tree[x].fa,z=tree[y].fa,l,r;
    if(tree[y].child[0]==x) l=0;else l=1;r=l^1;
    if(!isroot(y)){
        if(tree[z].child[0]==y)tree[z].child[0]=x;
        else                   tree[z].child[1]=x;
    }
    tree[x].fa=z;tree[y].fa=x;tree[tree[x].child[r]].fa=y;
    tree[y].child[l]=tree[x].child[r];tree[x].child[r]=y;
    pushup(y);pushup(x);
}
void splay(int x){
    Pushdown(x);
    while(!isroot(x)){
        int y=tree[x].fa,z=tree[y].fa;
        if(!isroot(y)){
            if((tree[y].child[0]==x)^(tree[z].child[0]==y)) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}
void access(int x){for(register int i=0;x;i=x,x=tree[x].fa){splay(x);tree[x].child[1]=i;pushup(x);}}
void reverse(int x){access(x);splay(x);tree[x].rev^=1;}
int findroot(int x){access(x);splay(x);while(tree[x].child[0])x=tree[x].child[0];return x;}
void link(int x,int y){reverse(x);tree[x].fa=y;}
void cnt(int x,int y){reverse(x);access(y);splay(y);if(tree[y].child[0]==x) tree[y].child[0]=tree[x].fa=0;}
int main(){
    scanf("%d%d",&n,&Q);
    for(register int i=1;i<=n;i++)scanf("%d",&tree[i].val),tree[i].sum=tree[i].val;
    while(Q--){
        scanf("%d%d%d",&opt,&xx,&yy);
        switch(opt){
            case 0:reverse(xx);access(yy);splay(yy); printf("%d\n",tree[yy].sum);break;
            case 1:if(findroot(xx)!=findroot(yy)) link(xx,yy);break;
            case 2:if(findroot(xx)==findroot(yy)) cnt(xx,yy);break;
            case 3:access(xx);splay(xx);tree[xx].val=yy;pushup(xx);break;
        }
    }
    return 0;
}

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值