Link-Cut-Tree模板


Update on 2017/4/17
多坑慎入……
这篇文章不仅没有讲什么虚实边,作者还是个智障。
大家如果要看的话建议先去参考其他人的TAT
窝理解清楚再补。。。QAQ


这次不是在很多人的帮助下……而是自己调的代码、、、
略微看了看自己LCT掉进去的几个坑、、、
略微介绍一下:
LCT的类似于树链剖分,只不过树链剖分是静态的,而LCT是动态的。
LCT用Splay来维护每一条重链,而且在初始情况下,LCT是没有轻重链之分的。
请注意,这里Splay维护的是按照深度递增的顺序来进行排序的。
找到一张图是这样的:
图侵删
把这个树变成LCT之后……
树变成LCT
核心操作:
Access(v):
访问一个节点,其作用是把该点到LCT的根的路径都变成重链,由于我们维护LCT的时候是用的Splay,我们要把v这个节点Splay到根,也就是Splay到LCT的总根,这是一切操作的基础。
Splay操作:
把一个点伸展到它所在重链的根
请注意一件事情,也是我最初极为困惑的事情。
在我们学习Splay的时候,我们的根节点的父亲是0,然而这里一条重链的根节点的父亲显然不是0,为了区分轻边重链,我们会使得根节点的父亲的左右儿子并不是根节点。
这里我找了网上的一张图。
aaa
这张图片里面,7的父亲是1,然而1是在浅红色重链里面的,即它是Splay的最左边节点(因为深度递增),所以它是没有左右儿子的,对吧?然而:7的父亲是1,这点却是一个不争的事实。
也就是说,我们通过什么来判断这个点是否是维护这条重链的Splay的根呢?
显然,我们只要看父亲节点的左右儿子里有没有它即可。
FRT(find root寻找原来的树的根)
请注意这里的root并不是LCT的根,而是原来那个树的根,切记切记。
大概我们来想想就是这样:
首先,根节点是深度最小的点。
所以我们考虑Access(v),v这里是任意一个节点。
然后Splay(v),走到它的最左端即可。
MRT(make root换根操作)
请注意,这里的换根操作也是原来的树的根。
所以说我们只需要这样就行了。
网上的图片……
换
我们把u换成v,也就意味着我们在保证其树的父子关系不变的情况下,使得v的深度最小。
那么也就意味着,我们只需要先Access(v),Splay(v),然后打个翻转标记即可。
这里要注意的一点是,如果询问跟固定一个根有关,在询问的时候一定要记住把根换回来……
Findroot可以找到当前原树的根,也就意味着我们把u换成v之后,Findroot的返回值就是v了。
Cut操作:
切断一条边。
显然在Cut的时候不是简单的找到两个点然后一划就行了的。
我们设Cut(u,v)。
MRT(u),Access(v),Splay(v)。
然后我们就会知道u一定在v的左儿子的位置,然后把v的左儿子标成0,u的父亲标成0即可。
Link操作:
连接一条边。
在连接之前我们肯定要先断掉之前的边『否则你的树形结构就被破坏了』。
我们考虑已经Cut完毕了,现在要连接(u,v),那么只需要:
MRT(u),t[u].par = v即可。
当然了……你随便把哪个当作父亲都是可以的。
模板题是弹飞绵羊。
问题在于……
询问的时候能处理什么?
至少能处理一种东西:
节点深度。
Access(v),Splay(v),return t[lc].sz即可。
BZOJ2002
『题意』
有一群弹簧(1~n),它们喜欢弹飞绵羊,每个弹簧可以让绵羊向后弹Ai格,如果绵羊被弹到超过n的范围,就算是被弹飞了,现在可以更改弹簧的后弹绵羊的能力,而有一个人会在某个弹簧上放一个绵羊,给出绵羊经过几次会被弹飞。
分析:
考虑我们虚拟一个节点标号为n+1,如果第i个弹簧能将绵羊弹飞到i + j,那么i - > min(i + j,n + 1)连边。
更改弹簧能力也就意味着Link&&Cut操作,放羊操作相当于询问节点的深度。
这题就这么过去了……第一次写的话调了好长时间。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#define Rep(i,n) for(int i = 1; i <= n ; i ++)
#define Repl(i,n) for(int i = 1,ls; i <= n ; i ++)
#define RepG(i,x) for(int i = head[x] ;~ i ; i = edge[i].next)
#define Rep_d(i,n) for(int i = n ; i > 0 ; i --)
#define Rep_0(i,n) for(int i = 0 ; i < n ; i ++)
#define RD(i,x,n) for(int i = x; i <= n ; i ++)
#define CLR(a,b) memset(a,b,sizeof(a))
#define RDD(i,x,n) for(int i = x; i >= n; i --)
#define u t[x]
#define debug puts("**")
#define debug2 puts("404 NOT FOUND")
#define lc ch[0]
#define rc ch[1]
#define tc ch[ty]
#define vc ch[!ty]
#define v edge[i].to
#define ulfc t[u.lc]
#define o t[y]
#define urtc t[u.rc]
#define p u.par
const int N = 200005;
using namespace std;
const int inf = 1 << 30;
typedef long long ll;
int read(){
    char ch = getchar();
    while(ch < '0' || ch > '9')ch = getchar ();
    int x = 0;
    while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar ();
    return x;
}
int n,m;
struct LCT{
    int ch[2],par,sz;
    bool rev;
    LCT(){sz = 1,ch[0] = ch[1] = par = 0;}
    void Rev(){rev ^= 1;swap(lc,rc);}
}t[N];
int sgn(int x){return t[p].lc == x ? 0 : t[p].rc == x ? 1 : -1;}
void sc(int x,int y,bool ty){u.tc = y,o.par = x;}
void Upd(int x){if(x)u.sz = ulfc.sz + urtc.sz + 1;}
void Dw(int x){
    if(u.rev)
        ulfc.Rev(),urtc.Rev(),u.rev ^= 1;
}
void Fix(int x){if(~sgn(x))Fix(p);Dw(x);}
void Rot(int x,bool ty){
    int y = p;
    //printf("ROTING:%d %d %d %d\n",x,y,o.par,ty);
    if(~sgn(y))sc(o.par,x,sgn(y));
    else p = o.par;
    sc(y,u.vc,ty),sc(x,y,!ty),Upd(y);
}
int Splay(int x){

    Fix(x);
    int d0,d1,y;
    //printf("x : %d x的父亲%d\n",x,p);
    //debug2;
    while((~(d0 = sgn(x)))){
        if(~(d1 = sgn(y = p)))Rot(d0 ^ d1 ? x : y,d0),Rot(x,d1);
        else Rot(x,d0);
    }
    //Rep_0(i,n + 2)
    //  printf("~~~%d %d %d %d~~\n",i,t[i].par,t[i].ch[0],t[i].ch[1]);
    return Upd(x),x;
}
int nxt[N];
int Acs(int tx){

//  puts("NOW : ACS");          
    for(int x = tx,y = 0;x;Upd(y = x),x = p)Splay(x),u.rc = y;
//  puts("ACS : ENDED");    
//  Rep_0(i,n + 2)
//      printf("%d %d %d %d %d\n",i,t[i].par,t[i].ch[0],t[i].ch[1],t[i].sz);
    return Splay(tx);
}
void MRT(int x){x = Acs(x);u.Rev();}
void Link(int x,int y){MRT(x),p = y;}
void DB(){
    puts("DEBUGING--------------------------------------------");
    Rep_0(i,n + 2)
        printf("%d %d %d %d %d\n",i,t[i].par,t[i].ch[0],t[i].ch[1],t[i].sz);
    puts("END-------------------------------------------");
}
void Cut(int x,int y){MRT(x),y = Acs(y),o.lc = 0,u.par = 0;}
int main (){
//  freopen("a.txt","r",stdin);
    t[0].sz = 0;//TMD初始化 
    n = read();
    Repl(i,n)
        nxt[i] = read(),t[i].par = min(i + nxt[i],n + 1),nxt[i] = min(nxt[i] + i,n + 1);
//  DB();
    //Rep_0(i,n + 2)
    //  printf("%d %d\n",i,t[i].par);
    m = read();
//  DB();
    Rep(i,m)
    {
        int op = read(),x = read();
//      puts("-------------------------------------------------------");
        x ++;
        if(op & 1){
        //  DB();
            MRT(n + 1);//为什么不换根呢少年!!!!!! 
            x = Acs(x);
            printf("%d\n",ulfc.sz);
    //      Rep_0(i,n + 2)
    //          printf("lalala: %d %d\n",i,t[i].sz);
        }
        else {
            int k = read();
        //  DB(); 
            x = Acs(x);//printf("CUT:%d %d\n",x,x + k);
        //  DB();
        //  printf("%d NXT:%d\n",x,nxt[x]);
            Cut(x,nxt[x]);//你要CUT 的不是你的左儿子啊!!!! nxt数组爆了啊!!!! 
        //  printf("%d NXT : %d\n",x,nxt[x]);
        //  DB();
            Link(x,min(x + k,n + 1));
            nxt[x] = min(x + k,n + 1);
        //  printf("%d NXT : %d\n",x,nxt[x]);
        //  DB();
    //      Rep_0(i,n + 2)
    //          printf("***%d %d %d %d\n",i,t[i].par,t[i].ch[0],t[i].ch[1]);
    //      printf("%d %d\n",x,x + k);

        }
    }
    return 0;
}
/*
10
4 5 4 3 1 3 1 1 1 3 
5
2 0 2 
2 7 1 
2 5 7 
1 6 
2 5 6 

10
6 7 10 2 5 5 4 4 10 7 
10
2 2 1 
2 2 4 
*/

大致错误犯了四个,第一个是忘记设置t[0]的sz为0,第二个是在询问的时候忘了换根,而至于第三个……
Cut错边了!!!!
Cut的时候一定要清楚你是Cut什么边……显然我们是找到两个相邻的节点去Cut,那么我们需要存一下上一次的x能被弹飞到哪里去。
第四个是比较2的错误,就是我nxt数组一开始取值为(i + nxt[i]),然而实际上那样会完美RE。
应该改为min(i + nxt[i],n + 1)。
模板就这样说完了。
部分转载自百度文库的一篇文章叫做
Link - Cut- Trees,写的很详细『当然可能有些人认为很废话』,感谢。
题库网站的话:
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=25242#overview
略微注意一下就是sone1是TopTree……
这网站好多错……看着做吧#233
略微调了一道题……
因为边带负权而我的快读一般都不带负数读入……
然后我就愉快地狗带了。
http://www.lydsy.com/JudgeOnline/problem.php?id=2157
BZOJ2157。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#define Rep(i,n) for(int i = 1; i <= n ; i ++)
#define Repl(i,n) for(int i = 1,ls; i <= n ; i ++)
#define RepG(i,x) for(int i = head[x] ;~ i ; i = edge[i].next)
#define Rep_d(i,n) for(int i = n ; i > 0 ; i --)
#define Rep_0(i,n) for(int i = 0 ; i < n ; i ++)
#define RD(i,x,n) for(int i = x; i <= n ; i ++)
#define CLR(a,b) memset(a,b,sizeof(a))
#define RDD(i,x,n) for(int i = x; i >= n; i --)
#define u t[x]
#define debug puts("**")
#define debug2 puts("404 NOT FOUND")
#define DEBUG puts("FREOPENING")
#define lc ch[0]
#define rc ch[1]
#define tc ch[ty]
#define vc ch[!ty]
#define v edge[i].to
#define ulfc t[u.lc]
#define o t[y]
#define urtc t[u.rc]
#define p u.par
using namespace std;
const int inf = 1 << 30;
const int N = 200005;
typedef long long ll;
int read(){
    char ch = getchar();
    while((ch < '0' || ch > '9')&& ch != '-')ch = getchar ();
    int x = 0,flag = 1;
    if(ch == '-')flag = -1,ch = getchar();
    while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar ();
    return x * flag;
}
int n,m,ed[N],idx;
struct LCT{
    int w,Max,Min,ch[2],par;
    ll sum;
    bool rev;
    bool tag;
    LCT(){w = sum = tag = 0;Max = -inf,Min = inf;}
    void Rev(){rev ^= 1;swap(lc,rc);}
    void TagR(){
        tag ^= 1;swap(Max,Min),w = -w,sum = - sum,Max = -Max,Min = -Min;
    }
    void TagW(int ps){
        sum -= w,w = ps,sum += w;
    }
}t[N << 2];
void DB(){/*puts("DEBUGING--------------------------------------------------");*/Rep_0(x,idx + 1)printf("x: %d sum: %lld w: %d lc: %d rc: %d p: %d\n",x,u.sum,u.w,u.lc,u.rc,p);}
void Upd(int x){
    u.sum = ulfc.sum + urtc.sum + u.w;
    u.Max = max(ulfc.Max,urtc.Max);
    if(x > n)u.Max = max(u.w,u.Max);
    u.Min = min(urtc.Min,ulfc.Min);
    if(x > n)u.Min = min(u.Min,u.w);
}
void Dw(int x){
    if(u.rev){
        ulfc.Rev(),urtc.Rev();u.rev ^= 1;
    }
    if(u.tag){
        ulfc.TagR(),urtc.TagR();u.tag ^=1;
    }
}
int sgn(int x){return t[p].lc == x ? 0 :t[p].rc == x ? 1 : -1;}
void sc(int x,int y,bool ty){u.tc = y;o.par = x;}
void Fix(int x){if(~sgn(x))Fix(p);Dw(x);}
void Rot(int x,bool ty){
    int y = p;
    if(~sgn(y))sc(o.par,x,sgn(y));
    else p = o.par;
    sc(y,u.vc,ty),sc(x,y,!ty),Upd(y);
}
int Splay(int x){
    Fix(x);
    int d0,d1,y;
    while((~(d0 = sgn(x)))){
        if(~(d1 = sgn(y = p)))Rot(d0 ^ d1 ? x : y,d0),Rot(x,d1);
        else Rot(x,d0);
    }
    return Upd(x),x;
}
int Acs(int tx){
    for(int x = tx,y = 0;x;Upd(y = x),x = p)Splay(x),u.rc = y;
    return Splay(tx);
}
void MRT(int x){Acs(x);u.Rev();}// 先Rev? 
void link(int x,int y){MRT(x),p = y;}
int main (){
//  freopen("lv.txt","r",stdin);
//  freopen("outp.txt","w",stdout);
    n = read();
    idx = n;
    Rep(i,n - 1){
        int a,b,c;
        ed[i] = ++ idx;
        a = read() + 1,b = read() + 1,c = read();
        link(a,idx),link(b,idx);
        t[idx].w = c;
        t[idx].Max = c;
        t[idx].Min = c;
        t[idx].sum = c;
    }
    m = read();
    char op[5];
    Rep(i,m){
        scanf("%s",op + 1);
        int a = read(),b = read();
        if(op[1] == 'C')
            Splay(ed[a]),t[ed[a]].w = b,Upd(ed[a]);
        else if(op[1] == 'N'){ //
            a ++ ,b ++;
            MRT(a);
            Acs(b);
            t[b].TagR();
        }
        else{  // Sum
            a ++ , b ++;
            MRT(a);
            Acs(b);
            if(op[1] == 'S')//Sum
                printf("%lld\n",t[b].sum);
            else if(op[2] == 'I')// Min
                printf("%d\n",t[b].Min);
            else if(op[2] == 'A')
                printf("%d\n",t[b].Max);
        }
    }
    return 0;
}
/*
5
0 1 32
0 4 45
0 3 37
1 2 76
3
N 0 3
SUM 0 1
MIN 0 2
*/

其实这道题没必要用LCT,然而就是想练模板……
然后还有一道题。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#define Rep(i,n) for(int i = 1; i <= n ; i ++)
#define RepG(i,x) for(int i = head[x] ;~ i ; i = edge[i].next)
#define Rep_d(i,n) for(int i = n ; i > 0 ; i --)
#define Rep_0(i,n) for(int i = 0 ; i < n ; i ++)
#define RD(i,x,n) for(int i = x; i <= n ; i ++)
#define CLR(a,b) memset(a,b,sizeof(a))
#define RDD(i,x,n) for(int i = x; i >= n; i --)
#define lc ch[0]
#define rc ch[1]
#define tc ch[ty]
#define vc ch[!ty]
#define u t[x]
#define o t[y]
#define p t[x].par
#define v edge[i].to
#define ulfc t[u.lc]
#define urtc t[u.rc]
const int N = 10005;
using namespace std;
const int inf = 1 << 30;
typedef long long ll;
inline int read(){
    char ch = getchar();
    while((ch < '0' || ch > '9') && ch != '-')ch = getchar ();
    int x = 0;
    bool flag = 1;
    if(ch == '-')ch = getchar(),flag = 0;
    while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar ();
    return flag ? x : -x;
}
int n,m;
struct LCT{int ch[2],par;bool rev;void Rev(){rev ^= 1;swap(lc,rc);}}t[N];
inline int sgn(int x){return t[p].ch[0] == x ? 0: t[p].ch[1] == x ? 1 : -1;}
inline void sc(int x,int y,bool ty){o.par = x;u.tc = y;}
inline void Upd(int x){;}
inline void Rot(int x,bool ty){
    int y ;
    if(~sgn(y = p))sc(o.par,x,sgn(y));
    else p = o.par;
    sc(y,u.vc,ty),sc(x,y,!ty),Upd(y);
}
inline void Dw(int x){
    if(u.rev)ulfc.Rev(),urtc.Rev(),u.rev ^= 1;
}
inline void Fix(int x){if(~sgn(x))Fix(p);Dw(x);}
int Splay(int x){
    Fix(x);
    int d0,d1,y;
    while(~(d0 = sgn(x))){
        if(~(d1 = sgn(y = p)))
            Rot(d0 ^ d1 ? x : y, d0),Rot(x,d1);
        else Rot(x,d0);
    }
    return Upd(x),x;
}
int Acs(int tx){for(int x = tx,y = 0 ;x;Upd(y = x),x = p)Splay(x),u.rc = y;return Splay(tx);}
inline void MRT(int x){Acs(x);u.Rev();}
inline void Link(int x,int y){MRT(x);p = y;} 
inline void Cut(int x,int y){MRT(x);Acs(y);o.lc = 0,u.par = 0;}
inline bool Find(int x,int y){while(p)x = p;while(o.par)y = o.par;return x == y;} 
int main (){
    n = read();
    m = read();
    Rep(i,m)
    {
        char op[15];
        scanf("%s",op + 1);
        int a = read(),b = read();
        if(op[1] == 'Q')
            puts(Find(a,b) ? "Yes" : "No");
        else if(op[1] == 'C')
            Link(a,b);
        else if(op[1] == 'D')
            Cut(a,b); 
    }
    return 0;
}


洞穴勘测Cave……
很水的一道题……也就是练模板了。
下面开始做真正的LCT吧,另开一文好了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值