BZOJ_3282_Tree_(LCT)
描述
http://www.lydsy.com/JudgeOnline/problem.php?id=3282
给出n个点以及权值,四种操作:
0.求x,y路径上的点权值的异或和.
1.连接x,y.
2.断开x,y.
3.将x的权值改为t.
分析
LCT模板题.
说几点自己的感悟和需要注意的地方吧(这里把原来的树称作树,平衡树称作Splay以避免混淆):
1.LCT是用Splay来维护一个森林(通过将链剖分),Splay以深度为关键字,即深度小的在左边,深度大的在右边.森林中每一棵树都有一个或多个Splay,刚开始的时候每个点都是一个独立的Splay.如果两个点x,y相连,假设x的深度小,则我们把y->pa设为x,但是注意并不把x的孩子设为y.这样一棵树中,还是每个点都是一个单独的Splay,不过与普通的Splay不同的是,这里的Splay的根并不一定上面连着null,而是它的父亲节点不连着它.
2.Access操作是LCT的最基本也是最重要的操作,它的作用是:如果我们Access(x),那么就会将x所在树的根节点到x的路径上的所有点放进一个Splay中.这样进行多次Access的话,一棵树就被分成了许多个Splay且每个Splay可能不再只代表一个点(可以自己画图看看).对于Splay的根节点,它的pa代表的是这个Splay(是一条链)的树上的祖先,而对于非Splay节点,它的父亲就是它在Splay中的父亲.这样我们就有了一个判断Splay根节点的方法:看它的pa是否连着它,如果连着,则这个点不是Splay的根,如果不连着,那么这个点就是Splay的根.当然,树的根节点就是父亲为null的点了.
另外注意Access(x)之后一定要Splay(x),(原来的x).原因如下(看不懂的先跳过去看下面的):
(1).在Access操作的时候,每次在树中深度小的Splay链上挂上深度大的点,都是挂在右孩子,但是都没有进行push_up()操作,所以之后进行一次Splay(x),x沿着右边转上去的过程中就把所有没有push_up的点都push_up了.(当然也可以在Access函数中每一步都push_up(),但好像会慢一点)
(2).保证在make_root(x)之后x同时也是Splay的根,这样在link函数里面make_root(x)之后才能直接x->pa=y.
(3).Access(x)之后无论进行什么有关x所在Splay的操作都要先对Splay中的某个点进行splay操作,x点在Splay的根节点的话,因为有fix函数,所以x位置的标记一定会传下去.如果我们make_root(x)过了的话,这样保证了x是最左边的也就是深度最小的,这样find_root函数才能找到正确树的根.
3.make_root操作.make_root(x)之后,x就是树的根了(有向树无此操作).我们思考会发现,对于树原来的根y和现在要成为根的x,我们只需要将y到x路径上的所有点的深度倒过来就可以了.所以我们Access(x),splay(x),x->rev^=true(将代表y到x这条链的Splay中的所有点深度倒转)即可.
4.link操作.make_root(x),然后直接连就行了(make_root的时候在Access之后进行了splay,所以x一定是Splay的根,所以可以直接连).
5.cut操作.make_root(x),Access(y),Splay(y)这样这个Splay中就只有x,y两个点,且x是y的左儿子,然后x->pa=null,y->ch[0]=null即可.
6.fix函数的重要性.一般的Splay在进行splay操作的时候可以边转边向下传标记(rev).但是LCT中的Splay是时时刻刻在变的,可能一会之后Splay中就不再是原来那些点了(多了一些新的,少了一些以前的).如果我们边转边传标记的话,会导致Splay中上面的标记没有完全传下来,一会如果下面的点换了(原来的点没了,新增了别的点),这个标记就出问题了.所以我们Splay操作的时候要先找到Splay的根,然后从上到下(直到要进行splay操作的点x),把标记全部放下来,这样保证了所有会影响x的子节点的标记都放干净了.因为每次Splay换点的时候都是先splay(x),再把x->ch[1]换掉,所以我们每次splay(x)的时候把会影响x子节点的标记都处理好,再换x的子节点,就不会有问题了.
7.因为Access之后一定要进行splay操作,所以我以后直接把splay写在Access函数里面了.
8.其实我觉得我讲的一点也不清楚...
#include <bits/stdc++.h>
using namespace std; const int maxn=+;
int n,m;
struct node{
int v,s; bool rev;
node* ch[],* pa;
node(int v,node* t):v(v),s(),rev(false){ ch[]=ch[]=pa=t; }
bool d(){ return pa->ch[]==this; }
bool c(){ return pa->ch[]==this||pa->ch[]==this; }//判断这个点有没有被父亲连着.
void setc(node* t,bool d){ ch[d]=t; t->pa=this; }
void push_up(){ s=ch[]->s^ch[]->s^v; }
void push_down(){
if(rev){
ch[]->rev^=true;
ch[]->rev^=true;
swap(ch[],ch[]);
rev=false;
}
}
}* t[maxn],* null;
void rot(node* o){
node* pa=o->pa; bool d=o->d();
pa->push_down(); o->push_down();
if(pa->c()) pa->pa->setc(o,pa->d());
else o->pa=pa->pa;
pa->setc(o->ch[!d],d);
o->setc(pa,!d);
pa->push_up();
}
void fix(node* o){
if(o->c()) fix(o->pa);
o->push_down();
}
void splay(node* o){
fix(o);//必须要找到根再放标记,否则标记下放不完全,splay又是动态的,会出错.
while(o->c())
if(!o->pa->c()) rot(o);
else o->d()==o->pa->d()?(rot(o->pa),rot(o)):(rot(o),rot(o));
o->push_up();
}
void access(node* x){//access之后一定要splay原来的x
node *y=null;
for(;x!=null;y=x, x=x->pa){
splay(x);
x->ch[]=y;
}
}
void make_root(node* o){
access(o);
splay(o);
o->rev^=true;
}
void link(node *u,node *v){
make_root(u);
u->pa=v;
}
void cut(node* u,node *v){
make_root(u);
access(v);
splay(v);
u->pa=null;
v->ch[]=null;
}
node* find_root(node* o){
access(o);
splay(o);
while(o->ch[]!=null) o=o->ch[];
return o;
}
int query(node *u,node *v){
make_root(u);
access(v);
splay(v);
return v->s;
}
void change(node *x,int y){
splay(x);
x->v=y;
x->push_up();
}
int main(){
null=new node(,NULL);
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
int x; scanf("%d",&x);
t[i]=new node(x,null);
}
for(int i=;i<=m;i++){
int c,x,y;
scanf("%d%d%d",&c,&x,&y);
if(c==) printf("%d\n",query(t[x],t[y]));
else if(c==){ if(find_root(t[x])!=find_root(t[y])) link(t[x],t[y]); }
else if(c==){ if(find_root(t[x])==find_root(t[y])) cut(t[x],t[y]); }
else if(c==) change(t[x],y);
}
return ;
}
3282: Tree
Time Limit: 30 Sec Memory Limit: 512 MB
Submit: 1417 Solved: 615
[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行,每行三个整数,分别代表操作类型和操作所需的量。
Output
对于每一个0号操作,你须输出X到Y的路径上点权的Xor和。
Sample Input
1
2
3
1 1 2
0 1 2
0 1 1
Sample Output
1
HINT
1<=N,M<=300000
Source
BZOJ_3282_Tree_(LCT)的更多相关文章
- 一堆LCT板子
搞了一上午LCT,真是累死了-- 以前总觉得LCT高大上不好学不好打,今天打了几遍感觉还可以嘛= =反正现在的水平应付不太难的LCT题也够用了,就这样好了,接下来专心搞网络流. 话说以前一直YY不出来 ...
- 动态树之LCT(link-cut tree)讲解
动态树是一类要求维护森林的连通性的题的总称,这类问题要求维护某个点到根的某些数据,支持树的切分,合并,以及对子树的某些操作.其中解决这一问题的某些简化版(不包括对子树的操作)的基础数据结构就是LCT( ...
- 在此为LCT开一个永久的坑
其实我连splay都还不怎么会. 今天先抄了黄学长的bzoj2049,以后一定要把它理解了. 写LCT怎么能不%数据结构大神yeweining呢?%%%chrysanthemums %%%切掉大森林 ...
- 【BZOJ2157】旅游 LCT
模板T,SB的DMoon..其实样例也是中国好样例...一开始不会复制,yangyang:找到“sample input”按住shift,按page down.... #include <ios ...
- 【BZOJ3669】[Noi2014]魔法森林 LCT
终于不是裸的LCT了...然而一开始一眼看上去这是kruskal..不对,题目要求1->n的路径上的每个点的两个最大权值和最小,这样便可以用LCT来维护一个最小生成路(瞎编的...),先以a为关 ...
- 【BZOJ1180】: [CROATIAN2009]OTOCI & 2843: 极地旅行社 LCT
竟然卡了我....忘记在push_down先下传父亲的信息了....还有splay里for():卡了我10min,但是双倍经验还是挺爽的,什么都不用改. 感觉做的全是模板题,太水啦,不能这么水了... ...
- 【BZOJ3282】Tree LCT
1A爽,感觉又对指针重怀信心了呢= =,模板题,注意单点修改时splay就好,其实按吾本意是没写的也A了,不过应该加上能更好维护平衡性. ..还是得加上好= = #include <iostre ...
- BZOJ2888 资源运输(LCT启发式合并)
这道题目太神啦! 我们考虑他的每一次合并操作,为了维护两棵树合并后树的重心,我们只好一个一个的把节点加进去.那么这样一来看上去似乎就是一次操作O(nlogn),但是我们拥有数据结构的合并利器--启发式 ...
- LCT裸题泛做
①洞穴勘测 bzoj2049 题意:由若干个操作,每次加入/删除两点间的一条边,询问某两点是否连通.保证任意时刻图都是一个森林.(两点之间至多只有一条路径) 这就是个link+cut+find roo ...
随机推荐
- [Guava官方文档翻译] 2.使用和避免使用null (Using And Avoiding Null Explained)
本文地址:http://www.cnblogs.com/hamhog/p/3536647.html "null很恶心." -Doug Lea "这是一个令我追悔莫及的错误 ...
- 堆排序 C++
1 堆排序拥有插入排序的优点 (是一种原地排序算法只需要存储常数个元素在输入数组以外 即省空间), 同时拥有合并排序算法的复杂度 nlgn,逼格有点高 2 堆数据结构 是一个数组对象,可以被视为一颗完 ...
- <<深入Java虚拟机>>-虚拟机类加载机制-学习笔记
类加载的时机 遇到new.getstatic.putstatic或invokestatic这4个字节码指令时,如果类没有进行过初始化,则需要先触发其初始化.生成这4条指令最常见的Java场景是:使用n ...
- Linux C 程序 输入输出函数(THREE)
标准输入输出函数#include<stdio.h>stdio 是 standard input & output 的缩写 字符数据输入输出函数: putchar() , getch ...
- 利用Keepalived+mysql构建高可用MySQL双主自动切转
转载:http://www.it300.com/index.php/article-15266.html 关于MySQL-HA,目前有多种解决方案,比如heartbeat.drbd.mmm.共享存储, ...
- svg学习笔记(一)
SVG——可扩展适量图形,基于XML PC端:IE9+ wap端:表现良好,适合使用 基础图形: line(线段) <line x1="25" y1="150 ...
- PHP 提取图片img标记中的任意属性
PHP 提取图片img标记中的任意属性的简单实例. 复制代码代码如下: <?php /* PHP正则提取图片img标记中的任意属性 */ $str = '<center><im ...
- (转载)Delphi TStringList的用法
Delphi TStringList的用法 TStrings是一个抽象类,在实际开发中,是除了基本类型外,应用得最多的. TStringList 常用方法与属性: var List: TStringL ...
- c#让窗体永在最前 调用windows api 将窗体设为topmost
有时候应用程序需要将一个窗体始终位于屏幕的最前面,即使切换到其它窗体也能看到该窗体,这样的窗体就叫做TopMost窗体. 用C#制作TopMost窗体之前,首先要了解如何声明SetWindowPos函 ...
- Spring MVC常用的注解
@Controller @Controller 负责注册一个bean 到spring 上下文中,bean 的ID 默认为 类名称开头字母小写,你也可以自己指定,如下 方法一: @Controller ...