• 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.其实我觉得我讲的一点也不清楚...

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 const int maxn=300000+5;
     5 int n,m;
     6 struct node{
     7     int v,s; bool rev;
     8     node* ch[2],* pa;
     9     node(int v,node* t):v(v),s(0),rev(false){ ch[0]=ch[1]=pa=t; }
    10     bool d(){ return pa->ch[1]==this; }
    11     bool c(){ return pa->ch[0]==this||pa->ch[1]==this; }//判断这个点有没有被父亲连着.
    12     void setc(node* t,bool d){ ch[d]=t; t->pa=this; }
    13     void push_up(){ s=ch[0]->s^ch[1]->s^v; }
    14     void push_down(){
    15         if(rev){
    16             ch[0]->rev^=true;
    17             ch[1]->rev^=true;
    18             swap(ch[0],ch[1]);
    19             rev=false;
    20         }
    21     }
    22 }* t[maxn],* null;
    23 void rot(node* o){
    24     node* pa=o->pa; bool d=o->d();
    25     pa->push_down(); o->push_down();
    26     if(pa->c()) pa->pa->setc(o,pa->d());
    27     else o->pa=pa->pa;
    28     pa->setc(o->ch[!d],d);
    29     o->setc(pa,!d);
    30     pa->push_up();
    31 }
    32 void fix(node* o){
    33     if(o->c()) fix(o->pa);
    34     o->push_down();
    35 }
    36 void splay(node* o){
    37     fix(o);//必须要找到根再放标记,否则标记下放不完全,splay又是动态的,会出错.
    38     while(o->c())
    39         if(!o->pa->c()) rot(o);
    40         else o->d()==o->pa->d()?(rot(o->pa),rot(o)):(rot(o),rot(o));
    41     o->push_up();
    42 }
    43 void access(node* x){//access之后一定要splay原来的x
    44     node *y=null;
    45     for(;x!=null;y=x, x=x->pa){
    46         splay(x);
    47         x->ch[1]=y;
    48     }
    49 }
    50 void make_root(node* o){
    51     access(o);
    52     splay(o);
    53     o->rev^=true;
    54 }
    55 void link(node *u,node *v){
    56     make_root(u);
    57     u->pa=v;
    58 }
    59 void cut(node* u,node *v){
    60     make_root(u);
    61     access(v);
    62     splay(v);
    63     u->pa=null;
    64     v->ch[0]=null;
    65 }
    66 node* find_root(node* o){
    67     access(o);
    68     splay(o);
    69     while(o->ch[0]!=null) o=o->ch[0];
    70     return o;
    71 }
    72 int query(node *u,node *v){
    73     make_root(u);
    74     access(v);
    75     splay(v);
    76     return v->s;
    77 }
    78 void change(node *x,int y){
    79     splay(x);
    80     x->v=y;
    81     x->push_up();
    82 }
    83 int main(){
    84     null=new node(0,NULL);
    85     scanf("%d%d",&n,&m);
    86     for(int i=1;i<=n;i++){
    87         int x; scanf("%d",&x);
    88         t[i]=new node(x,null);
    89     }
    90     for(int i=1;i<=m;i++){
    91         int c,x,y;
    92         scanf("%d%d%d",&c,&x,&y);
    93         if(c==0) printf("%d
    ",query(t[x],t[y]));
    94         else if(c==1){ if(find_root(t[x])!=find_root(t[y])) link(t[x],t[y]); }
    95         else if(c==2){ if(find_root(t[x])==find_root(t[y])) cut(t[x],t[y]); }
    96         else if(c==3) change(t[x],y);
    97     }
    98     return 0;
    99 }
    View Code

    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

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

    Sample Output

    3
    1

    HINT

    1<=N,M<=300000

    Source

  • 相关阅读:
    hdu4717 The Moving Points(二分做法)
    C++中用rand()和srand()产生随机数方法介绍
    教你看懂C++类库函数定义之一---HRESULT 宏
    [置顶] IOS培训资料
    调试出不来 断点不起作用 调试技巧 MyEclipse进不了调试
    [置顶] 编程模仿boost::function和boost::bind
    模拟红外协议接收程序
    Java 使用JDBC、DBCP、C3P0访问数据库
    Linux点亮一个灯
    Makefile解析(最简单的LED)
  • 原文地址:https://www.cnblogs.com/Sunnie69/p/5536356.html
Copyright © 2020-2023  润新知