[BZOJ3282]Tree(LCT)

 

3282: Tree

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 2350  Solved: 1104
[Submit][Status][Discuss]

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行,每行三个整数,分别代表操作类型和操作所需的量。
1<=N,M<=300000

 

 

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

Source

复习一下LCT板子。感觉异或有非常多的性质?至少异或和LCT关系比较密切。

Link-Cut Tree,一种可以支持在线维护森林以及连边,断边等森林操作的数据结构。主要难点在于区分“原树”与“Splay”树。

和Splay的区别不大,主要是Rot()和Splay()中的y!=rt改为了!isroot(y),以及Splay之前一定要记得将这棵Splay树pushdown。

注意要多upd(),修改的时候记得Splay()一下以保证期望复杂度。

for (int y=0; x; y=x,x=f[x]) splay(x),ch[x][1]=y,upd(x); 感觉是最难的一句话,要记牢。

代码应该会很难调,所以要提高熟练度和静态差错的能力。

有个奇怪的地方,在结构体内定义的函数好像可以不用分先后顺序。如下面的代码,split()用到了mkroot(),但前者可以放在后者的前面。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define ls ch[x][0]
 4 #define rs ch[x][1]
 5 #define rep(i,l,r) for (int i=l; i<=r; i++)
 6 using namespace std;
 7 
 8 const int N=300100;
 9 int n,Q,op,x,y;
10 
11 struct LCT{
12     int v[N],sum[N],f[N],ch[N][2],rev[N];
13     bool isroot(int x){ return (!f[x]) || (ch[f[x]][0]!=x && ch[f[x]][1]!=x); }
14     void put(int x){ swap(ls,rs); rev[x]^=1; }
15     void push(int x){ if (rev[x]) put(ls),put(rs),rev[x]=0; }
16     void upd(int x){ sum[x]=sum[ls]^sum[rs]^v[x]; }
17     void pd(int x){ if (!isroot(x)) pd(f[x]); push(x); }
18     
19     void rot(int x){
20         int y=f[x],z=f[y],w=(ch[y][1]==x);
21         if (!isroot(y)) ch[z][ch[z][1]==y]=x;
22         f[x]=z; f[y]=x; f[ch[x][w^1]]=y;
23         ch[y][w]=ch[x][w^1]; ch[x][w^1]=y; upd(y);
24     }
25     
26     void splay(int x){
27         pd(x);
28         while (!isroot(x)){
29             int y=f[x],z=f[y];
30             if (!isroot(y)){ if ((ch[z][0]==y) ^ (ch[y][0]==x)) rot(x); else rot(y); }
31             rot(x);
32         }
33         upd(x);
34     }
35     
36     void access(int x){ for (int y=0; x; y=x,x=f[x]) splay(x),ch[x][1]=y,upd(x); }
37     void split(int x,int y){ mkroot(x); access(y); splay(y); }
38     void mkroot(int x){ access(x); splay(x); put(x); }
39     void link(int x,int y){ mkroot(x); f[x]=y; }
40     void cut(int x,int y){ split(x,y); ch[y][0]=f[x]=0; upd(y); }
41     int find(int x){ access(x); splay(x); while (ls) x=ls; return x; }
42 }T;
43 
44 int main(){
45     freopen("bzoj3282.in","r",stdin);
46     freopen("bzoj3282.out","w",stdout);
47     scanf("%d%d",&n,&Q);
48     rep(i,1,n) scanf("%d",&T.v[i]);
49     while (Q--){
50         scanf("%d%d%d",&op,&x,&y);
51         if (op==0) T.split(x,y),printf("%d\n",T.sum[y]);
52         if (op==1) if (T.find(x)!=T.find(y)) T.link(x,y);
53         if (op==2) if (T.find(x)==T.find(y)) T.cut(x,y);
54         if (op==3) T.v[x]=y,T.splay(x);
55     }
56     return 0;
57 }

 

转载于:https://www.cnblogs.com/HocRiser/p/8544721.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值