Link Cut Tree

Link Cut Tree

LCT专题总结

Part 1 LCT 原理介绍

(以下均为个人见解)

LCT是一种树链剖分,奇怪的树链剖分

根据我的认知,LCT是基于将整颗树随机地剖成若干条能够动态改变的链,每条链均是一条到达祖先的路径

整棵LCT就是由这样的链构成,并且这些链可以动态重构和连接

LCT最核心的操作就是\(Access\)

如何理解这个操作呢?

\(Access(x)\)就是构造出一条\(x\)到根的路径构成的链,并且构造完成后链上没有\(x\)的儿子

算法实现起来就是:

切掉\(x\)的儿子

\(while\) \(x\)所在链的顶端不是\(root\)

​ 切掉\(fa[top[x]\)]的儿子

​ 将\(x\)所在的链接在\(fa[top[x]]\)下面

我们亲爱的 \(tarjan\)老先生 证明了这个复杂度是\(log(n)\)

那如何搞定这个断链接链的操作呢?

\(Splay\)来搞定就行了

这样,我们的\(LCT\)算法,就是将原来的树,剖成了若干条链,每条链由一棵\(Splay\)维护,按照深度构树

Part 2 LCT基本操作

说的轻巧,写起来还是贼**

首先我么考虑存储

通常我们使用的\(LCT\)写法都是讲\(Splay\)里的\(fa\)数组和链与链之间的\(fa\)数组放在了一起

也就是说:

如果这个节点是它所在\(Splay\)的根,那么它的\(fa\)就是树上的\(fa\)

否则就是\(Splay\)上的\(fa\)

为了辨别它是不是Splay的跟,我们又引入了一个函数

inline int isroot(int x){ return !fa[x]||(son[fa[x]][1]!=x&&son[fa[x]][0]!=x); }

\(Access\)

现在我们来看\(Access\)操作的代码实现

//while version
void Access(int x){
    int t=0;
    while(x){
        Splay(x);
        son[x][1]=t; 
        // 这里的这个操作不仅是将t接到了x上,同时也能把x的儿子断掉
        x=fa[x];
    }
}

//for version
void Access(int x){ for(int t=0;x;t=x,x=fa[x]) Splay(x),son[x][1]=t; }

tips:如果这道题有\(Up()\)操作,\(Access\)时别忘了

好我们先练几道模板题

[BZOJ 2002] Bounce 弹飞绵羊

就是维护深度,支持动态连边

#include
#include
#include
#include
#include
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)


char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=2e5+10;

int n,m;
int fa[N],son[N][2],top[N],sz[N];

inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][1]!=x&&son[fa[x]][0]!=x); }

void Up(int u) {
    if(!u) return;
    sz[u]=sz[son[u][0]]+sz[son[u][1]]+1;
}

void rotate(int u) {
    int f=fa[u],ff=fa[f],d=dir(u);
    fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
    son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
    fa[f]=u,son[u][!d]=f;
    Up(f),Up(u),Up(ff);
}
void Splay(int u) {
    if(!u) return;
    Up(u);
    while(!isroot(u)) {
        int f=fa[u];
        if(!isroot(f)) {
            if(dir(u)^dir(f)) rotate(u);
            else rotate(f);
        }
        rotate(u);
    }
}

void Access(int x) {
    int t=0;
    while(x) {
        Splay(x);
        son[x][1]=t;
        Up(x);
        x=fa[x];
    }
}

int Que(int x) {
    Access(x);
    Splay(x);
    return sz[son[x][0]];
}

void Link(int x,int to) {
    Splay(x);
    fa[son[x][0]]=fa[x],son[x][0]=0;
    fa[x]=to;
}

int main(){
    rep(i,1,n=rd()) {
        int x=min(n+1,rd()+i);
        fa[i]=x;
        Up(i);
    }
    Up(n+1);
    rep(i,1,rd()) {
        int opt=rd();
        if(opt==1) printf("%d\n",Que(rd()+1));
        else {
            int x=rd()+1,t=rd();
            Link(x,min(n+1,x+t));
        }
    }
}

\[ \ \]

\[ \ \]

\(MakeRoot\)操作

即改变\(LCT\)的根

(由于博主能力有限,请读者画图自行理解一下)

void MakeRoot(int x){
    Access(x);
    Splay(x);
    rev[x]^=1;
    swap(son[x][0],son[x][1]);
}

就是构造一条到跟的链,然后将这条链翻转,翻转之后就能够让\(x\)变成根,同时树的其他部分并不需要改变

这个操作就要打标记了

[BZOJ 2049] Cave 洞穴勘测

这道题要多一个\(GetRoot\)操作,这个也不必多讲了吧

#include
#include
#include
#include
#include
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)


char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=1e4+10;

int n,m;
int fa[N],son[N][2];
int rev[N];

inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x] || (son[fa[x]][0]!=x && son[fa[x]][1]!=x); }

void Down(int u) {
    if(!u || !rev[u]) return;
    int t;
    if(son[u][0]) {
        t=son[u][0];
        rev[t]^=1;
        swap(son[t][0],son[t][1]);
    }
    if(son[u][1]) {
        t=son[u][1];
        rev[t]^=1;
        swap(son[t][0],son[t][1]);
    }
    rev[u]=0;
}

void rotate(int u) {
    int f=fa[u],ff=fa[f];
    if(!isroot(f)) Down(ff);
    Down(f),Down(u);
    int d=dir(u);
    fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
    son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
    fa[f]=u,son[u][!d]=f;
}
void Splay(int u) {
    Down(u);
    while(!isroot(u)) {
        int f=fa[u];
        if(!isroot(f)) {
            if(dir(u)^dir(f)) rotate(u);
            else rotate(f);
        }
        rotate(u);
    }
}

void Access(int x) {
    int t=0;
    while(x) {
        Splay(x);
        son[x][1]=t,fa[t]=x,t=x;
        x=fa[x];
    }
}

void makeroot(int x) {
    Access(x);
    Splay(x);
    rev[x]^=1;
    swap(son[x][0],son[x][1]);
}

int GetRoot(int x){ 
    Access(x),Splay(x);
    Down(x);
    while(son[x][0]) {
        x=son[x][0];
        Down(x);
    }
    Splay(x);
    return x;
}

int Que(int x,int y) {
    return GetRoot(x)==GetRoot(y);
}

void Link(int x,int y) {
    makeroot(x);
    fa[x]=y;
}

void Cut(int x,int y) {
    makeroot(x);
    Access(y);
    Splay(y);
    fa[x]=0;
    son[y][0]=0;
}


char opt[10];

int main(){
    n=rd(),m=rd();
    rep(i,1,m) {
        scanf("%s",opt);
        if(opt[0]=='Q') puts(Que(rd(),rd())?"Yes":"No");
        else if(opt[0]=='C') Link(rd(),rd());
        else Cut(rd(),rd());
    }
}

\[ \ \]

\[ \ \]

路径查询

据我所知,路径查询有两种写法

一种是先\(Access(x)\),再把\(y\) \(Access\)\(x\)下面(部分\(Access\)),然后就有几种情况讨论一下

当然可以直接就\(MakeRoot(x),Access(y),Splay(y)\),直接访问,简单粗暴,但是要打标记

[SPOJ QTREE] Query on a tree

树剖线段树裸题。。。

但是\(LCT\)\(log\)!

由于边是静态的,所以就是一个简单的路径查询

这里我唯一一次写了第一种查询

#include
#include
#include
#include
#include
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)


char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=1e4+10;

int n,m;
int fa[N],son[N][2],s[N],val[N];
int id[N],num[N];

inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][0]!=x&&son[fa[x]][1]!=x); }


void Up(int u) {
    s[u]=max(val[u],max(s[son[u][0]],s[son[u][1]]));
}

void rotate(int u){
    int f=fa[u],ff=fa[f],d=dir(u);
    int t=isroot(f);
    fa[u]=ff; if(!t) son[ff][dir(f)]=u;
    son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
    fa[f]=u,son[u][!d]=f;
    Up(f),Up(u);
    if(!t) Up(ff); 
}
void Splay(int u) {
    while(!isroot(u)) {
        int f=fa[u];
        if(!isroot(f)) {
            if(dir(u)^dir(f)) rotate(u);
            else rotate(f);
        }
        rotate(u);
    }
}

int Que(int x,int y) {
    if(x==y) return 0;
    int t1=0,t2=0,tx=x;
    while(x) {
        Splay(x);
        son[x][1]=t1;
        Up(x); t1=x; x=fa[x];
    }
    while(y) {
        Splay(y);
        if(!fa[y]) break;
        son[y][1]=t2;
        Up(y); t2=y; y=fa[y];
    }
    if(!t2) return s[son[y][1]];
    int ans=s[t2];
    if(t2==tx) return ans;
    ans=max(ans,s[son[y][1]]);
    return ans;
}

struct Edge{
    int to,nxt,w,id;
}e[N<<1];
int head[N],ecnt;
void AddEdge(int u,int v,int w,int id) {
    e[++ecnt]=(Edge){v,head[u],w,id};
    head[u]=ecnt;
}
void pre_dfs(int u,int f) {
    son[u][0]=son[u][1]=0;
    for(int i=head[u];i;i=e[i].nxt) {
        int v=e[i].to;
        if(v==f) continue;
        id[e[i].id]=v,fa[v]=u;
        s[v]=val[v]=e[i].w;
        pre_dfs(v,u);
    }
}
char opt[10];
int main(){
    rep(kase,1,rd()) {
        n=rd();
        rep(i,1,n) head[i]=0; ecnt=0;
        rep(i,1,n-1) {
            int u=rd(),v=rd(),w=rd();
            AddEdge(u,v,w,i);
            AddEdge(v,u,w,i);
        }
        fa[1]=0;
        pre_dfs(1,0);
        while(~scanf("%s",opt) && opt[0]!='D') {
            if(opt[0]=='Q') printf("%d\n",Que(rd(),rd()));
            else {
                int x=rd(),y=rd();
                val[id[x]]=y;
                Splay(id[x]);
            }
        }
    }
}

\[ \ \]

\[ \ \]

HDU - 4010

多了几个操作,\(Debug\)警告!

#include
#include
#include
#include
#include
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)




char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=3e5+10;

int n,m;
int fa[N],son[N][2];
int s[N],val[N],t[N];
int rev[N];

struct Edge{
    int to,nxt;
}e[N<<1];
int head[N],ecnt;
void AddEdge(int u,int v){
    e[++ecnt]=(Edge){v,head[u]};
    head[u]=ecnt;
}

inline int dir(int x) { return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x] || (son[fa[x]][0]!=x && son[fa[x]][1]!=x); }

void Down(int u) {
    if(!u) return;
    if(rev[u]) {
        if(son[u][0]) {
            rev[son[u][0]]^=1;
            swap(son[son[u][0]][0],son[son[u][0]][1]);
        }
        if(son[u][1]) {
            rev[son[u][1]]^=1;
            swap(son[son[u][1]][0],son[son[u][1]][1]);
        }
        rev[u]=0;
    }
    if(t[u]) {
        if(son[u][0]) {
            t[son[u][0]]+=t[u];
            s[son[u][0]]+=t[u];
            val[son[u][0]]+=t[u];
        }
        if(son[u][1]) {
            t[son[u][1]]+=t[u];
            s[son[u][1]]+=t[u];
            val[son[u][1]]+=t[u];
        }
        t[u]=0;
    }
}
void Up(int u) {
    if(!u) return;
    s[u]=max(val[u],max(s[son[u][0]],s[son[u][1]]));
}

void rotate(int u) {
    int t=!isroot(fa[u]);
    if(t) Down(fa[fa[u]]);
    Down(fa[u]),Down(u);
    int f=fa[u],ff=fa[f],d=dir(u);
    fa[u]=ff; if(t) son[ff][dir(f)]=u;
    son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
    fa[f]=u,son[u][!d]=f;
    Up(f),Up(u);
    if(t) Up(ff);
}
void Splay(int u) {
    static int stk[N],top=0;
    int t=u;
    while(1) {
        stk[++top]=t;
        if(isroot(t)) break;
        t=fa[t];
    }
    while(top) Down(stk[top--]);
    Down(u);
    while(!isroot(u)) {
        int f=fa[u];
        if(!isroot(f)) {
            if(dir(u)^dir(f)) rotate(u);
            else rotate(f);
        }
        rotate(u);
    }
}

void Access(int x) {
    int t=0;
    while(x) {
        Splay(x);
        son[x][1]=t;
        Up(x);
        t=x,x=fa[x];
    }
}

void pre_dfs(int u,int f) {
    fa[u]=f;
    son[u][0]=son[u][1]=rev[u]=t[u]=0;
    for(int i=head[u];i;i=e[i].nxt) {
        int v=e[i].to;
        if(v==f) continue;
        pre_dfs(v,u);
    }
}

int GetRoot(int x) {
    Access(x);
    Splay(x);
    while(1) {
        Down(x);
        if(!son[x][0]) break;
        x=son[x][0];
    }
    Splay(x);
    return x;
}
void MakeRoot(int x) {
    Access(x);
    Splay(x);
    rev[x]^=1;
    swap(son[x][0],son[x][1]);
}

void Link(int x,int y) {
    if(GetRoot(x)==GetRoot(y)) return (void)puts("-1");
    MakeRoot(x);
    fa[x]=y;
}
void Cut(int x,int y) {
    swap(x,y);
    if(x==y || GetRoot(x)!=GetRoot(y)) return (void)puts("-1");
    MakeRoot(x);
    Access(y);
    Splay(y);
    fa[son[y][0]]=0;
    son[y][0]=0;
}

void Add(int x,int y,int w) {
    if(GetRoot(x)!=GetRoot(y)) return (void)puts("-1");
    MakeRoot(x);
    Access(y);
    Splay(y); 
    s[y]+=w,t[y]+=w,val[y]+=w;
}

int Que(int x,int y){
    if(GetRoot(x)!=GetRoot(y)) return -1;
    MakeRoot(x);
    Access(y);
    Splay(y);
    return s[y];
}

int main(){
    while(~scanf("%d",&n)) {
        rep(i,1,n) head[i]=0; ecnt=0;
        rep(i,2,n) {
            int u=rd(),v=rd();
            AddEdge(u,v);
            AddEdge(v,u);
        }
        rep(i,1,n) s[i]=val[i]=rd();
        pre_dfs(1,0);
        rep(ttt,1,m=rd()) {
            int opt=rd();
            if(opt==1) Link(rd(),rd());
            if(opt==2) Cut(rd(),rd());
            if(opt==3) {
                int w=rd(),x=rd(),y=rd();
                Add(x,y,w);
            }
            if(opt==4) printf("%d\n",Que(rd(),rd()));
        }
        puts("");
    }
}



\[ \ \]

\[ \ \]

HDU - 5052

城市贸易动态版

如果要\(MakeRoot\)打翻转标记的话,存一个正的存一个反的直接换就行了

#include
#include
#include
#include
#include
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=5e4+10;

int n,m;
int fa[N],son[N][2];

inline int max(int x,int y) { return x>y?x:y; }
inline int min(int x,int y) { return x

HDU - 5398

这题需要我们弄一些小性质

假设我们从大的数向小的数连边,那么一定是向大的数的因子连边

如果这条边已经存在,那么就要从当前产生的环上换下来,存一个最小值

这样就是\(LCT\)模拟连边过程,总复杂度$n \cdot log  n \cdot ln  n $

#include
#include
#include
#include
#include
#include
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=2e5+10;

int n=1e5;
vector  fac[N];
int fa[N],son[N][2];
int rev[N];
struct Node{
    int x,id;
    Node operator + (const Node t) const{
        Node res;
        res.x=min(x,t.x);
        if(res.x==x) res.id=id;
        else res.id=t.id;
        return res;
    }
} s[N],val[N];

inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x] || (son[fa[x]][0]!=x&&son[fa[x]][1]!=x); }

void Up(int u) {
    if(!u) return;
    s[u]=val[u];
    if(son[u][0]) s[u]=s[u]+s[son[u][0]];
    if(son[u][1]) s[u]=s[u]+s[son[u][1]];
}
void Down(int u) {
    if(!u||!rev[u]) return;
    int t;
    if(son[u][0]) {
        t=son[u][0];
        rev[t]^=1;
        swap(son[t][0],son[t][1]);
    }
    if(son[u][1]) {
        t=son[u][1];
        rev[t]^=1;
        swap(son[t][0],son[t][1]);
    }
    rev[u]=0;
}

int stk[N],top;
int eu[N],ev[N];

void rotate(int u) {
    int f=fa[u],ff=fa[f];
    Down(ff),Down(f),Down(u);
    int d=dir(u);
    fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
    son[f][d]=son[u][!d]; fa[son[u][!d]]=f;
    fa[f]=u,son[u][!d]=f;
    Up(f),Up(u),Up(ff);
}

void Splay(int x) {
    Down(x),Up(x);
    static int stk[N],top=0;
    int t=x;
    while(!isroot(t)) stk[++top]=t,t=fa[t];
    stk[++top]=t;
    while(top) Down(stk[top--]);
    while(!isroot(x)) {
        int f=fa[x];
        if(!isroot(f)) {
            if(dir(x)^dir(f)) rotate(x);
            else rotate(f);
        }
        rotate(x);
    }
}



void Access(int x){ for(int t=0; x; t=x,x=fa[x]) Splay(x),son[x][1]=t,Up(x); }
void MakeRoot(int x) {
    Access(x);
    Splay(x);
    rev[x]^=1;
    swap(son[x][0],son[x][1]);
}

int GetRoot(int x) { 
    Access(x);
    Splay(x);
    do {
        Down(x);
        if(!son[x][0]) break;
        x=son[x][0];
    } while(1);
    Splay(x);
    return x;
}


void Cut(int x,int y) {
    MakeRoot(x);
    Access(y);
    Splay(x);
    fa[y]=0;
    son[x][1]=0;
    Up(x);
}


ll ans;
ll res[N];



void Link(int x,int y,int w) {
    if(GetRoot(x)==GetRoot(y)) {
        MakeRoot(x);
        Access(y);
        Node t=s[y];
        if(t.x>=w) return;
        ans-=t.x;
        Cut(eu[t.id],t.id);
        Cut(ev[t.id],t.id);
        son[t.id][0]=son[t.id][1]=fa[t.id]=rev[t.id]=0;
        stk[++top]=t.id;
    }
    MakeRoot(y);
    int t=stk[top--];
    fa[t]=rev[t]=0,son[t][0]=son[t][1]=0,s[t]=val[t]=(Node){w,t};
    eu[t]=x,ev[t]=y;
    ans+=w;
    fa[t]=x;
    fa[y]=t;
}



int main(){
    rep(i,n+1,2*n) stk[++top]=i;
    rep(i,1,n) s[i]=val[i]=(Node){(int)1e9,i};
    for(reg int i=1;i<=n;++i) for(reg int j=i+i;j<=n;j+=i) fac[j].push_back((int)i);
    rep(i,2,n) {
        drep(j,fac[i].size()-1,0) Link(i,fac[i][j],fac[i][j]);
        res[i]=ans;
    }
    while(~scanf("%d",&n)) printf("%lld\n",res[n]);
}



HDU - 5333

询问离线,从左到右,每次将\(max(u,v) \leq i\)的边加入,同时按照\(min(u,v)\)较大的顺序将边保留,保证当前图是一棵生成树,并且图上的边是每一个环上最大的(即动态维护最大生成树),用树状数组维护边权\(\geq j\)的总数,即联通块减少的数量

#include
#include
#include
#include
#include
#include
#include
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}
const int N=2e5+10;

int n,m,q;
struct Edge{
    int u,v;
    int operator < (const Edge __) const {
        return max(u,v)=w) return;
        Cut(eu[t.id],t.id);
        Cut(ev[t.id],t.id);
        B.Add(t.x,-1);
        stk[++top]=t.id;
    }
    MakeRoot(y);
    int t=stk[top--];
    eu[t]=x,ev[t]=y;
    son[t][0]=son[t][1]=rev[t]=fa[t]=0,s[t]=val[t]=(Node){w,t};
    fa[t]=x;
    fa[y]=t;
    B.Add(w,1);
}

int main(){
    while(~scanf("%d%d%d",&n,&m,&q)) {
        top=0;
        rep(i,n+1,n*2) stk[++top]=i;
        rep(i,1,n*2) fa[i]=son[i][0]=son[i][1]=rev[i]=0;
        rep(i,1,n) s[i]=val[i]=(Node){(int)1e9,0};
        rep(i,1,m) E[i].u=rd(),E[i].v=rd();
        sort(E+1,E+m+1);
        rep(i,1,q) Q[i].l=rd(),Q[i].r=rd(),Q[i].id=i;
        sort(Q+1,Q+q+1);
        int p1=1,p2=1;
        B.init();
        rep(i,1,n) {
            while(p1<=m && E[p1].u<=i&&E[p1].v<=i) {
                Link(E[p1].u,E[p1].v,min(E[p1].u,E[p1].v));
                p1++;
            }
            while(p2<=q && Q[p2].r<=i) {
                Ans[Q[p2].id]=B.Que(Q[p2].r)-B.Que(Q[p2].l-1);
                p2++;
            }
        }
        rep(i,1,q) printf("%d\n",n-Ans[i]);
    }
}


\[ \ \]

\[ \ \]

HDU - 5967

动态维护基环内向树

将树边直接加入,非树边更新根的\(mark\)

查询时\(GetRoot\)然后根据\(mark\)值判断

#include
#include
#include
#include
#include
#include
#include
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=2e5+10;

int n,m;
int fa[N],son[N][2];

inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][0]!=x&&son[fa[x]][1]!=x); }
void rotate(int u){
    int f=fa[u],ff=fa[f],d=dir(u);
    fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
    son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
    fa[f]=u; son[u][!d]=f;
}
void Splay(int x) {
    while(!isroot(x)) {
        int f=fa[x];
        if(!isroot(f))  {
            if(dir(x)^dir(f)) rotate(x);
            else rotate(f);
        }
        rotate(x);
    }
}

void Access(int x){ for(int t=0;x;t=x,x=fa[x]) Splay(x),son[x][1]=t; }

int GetRoot(int x) {
    Access(x),Splay(x);
    while(son[x][0]) x=son[x][0];
    Splay(x);
    return x;
}
int mk[N];

void Link(int x,int to) {
    if(!to) return;
    if(GetRoot(x)==GetRoot(to)) {
        mk[x]=to;
        return;
    } else mk[x]=0;
    Splay(x);
    fa[x]=to;
}

void Cut(int x) {
    int rt=GetRoot(x);
    if(x==rt) {
        mk[x]=0;
        return;
    }
    Access(x);
    Splay(x);
    fa[son[x][0]]=0;
    son[x][0]=0;
    if(mk[rt]) Link(rt,mk[rt]);
}


int main(){
    n=rd(),m=rd();
    rep(i,1,n) Link(i,rd());
    rep(i,1,m) {
        int opt=rd();
        if(opt==1) {
            int x=rd();
            Cut(x);
            Link(x,rd());
        } else {
            int rt=GetRoot(rd());
            if(mk[rt]) puts("-1");
            else printf("%d\n",rt);
        }
    }
}

\[ \ \]

\[ \ \]

[BZOJ - 2759] 一个动态树好题

这个题其实也和上一题差不多

但是要维护路径迭代和,同时mark数组也要存非树边

#include
#include
#include
#include
#include
#include
#include
using namespace std;
 
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
 
 
 
 
char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}
 
const int N=2e5+10,P=1e4+7;
 
 
int n,m;
 
int fa[N],son[N][2];
 
struct Node{
    int x,y;
    Node operator + (const Node t) const {
        return (Node){x*t.x%P,(t.x*y+t.y)%P};
    }
} s[N],val[N],t[N];
 
 
 
inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][1]!=x&&son[fa[x]][0]!=x); }
 
int Inv[P];
int mk[N];
 
void Up(int u) {
    if(!u) return;
    s[u]=val[u];
    if(son[u][0]) s[u]=s[son[u][0]]+s[u];
    if(son[u][1]) s[u]=s[u]+s[son[u][1]];
}
 
void rotate(int u){
    int f=fa[u],ff=fa[f],d=dir(u);
    fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
    son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
    fa[f]=u; son[u][!d]=f;
    Up(f),Up(u),Up(ff);
}
void Splay(int u) {
    while(!isroot(u)) {
        int f=fa[u];
        if(!isroot(f)) {
            if(dir(u)^dir(f)) rotate(u);
            else rotate(f);
        }
        rotate(u);
    }
}
 
void Access(int x){ for(reg int t=0;x;t=x,x=fa[x]) Splay(x),son[x][1]=t,Up(x); }
 
int GetRoot(int x) {
    Access(x);
    Splay(x);
    while(son[x][0]) x=son[x][0];
    Splay(x);
    return x;
}
 
void Link(int x,int to) {
    s[x]=val[x];
    if(GetRoot(x)==GetRoot(to)) {
        t[x]=val[x];
        s[x]=val[x]=(Node){1,0};
        mk[x]=to;
        return;
    }
    Access(x);
    Splay(x);
    fa[x]=to;
}
 
void Cut(int x) {
    if(mk[x]) {
        mk[x]=0;
        s[x]=val[x]=t[x];
        return;
    }
    int rt=GetRoot(x);
    Access(x);
    Splay(x);
    fa[son[x][0]]=0;
    son[x][0]=0;
    if(mk[rt] && GetRoot(rt)!=GetRoot(mk[rt])) {
        s[rt]=val[rt]=t[rt];
        Link(rt,mk[rt]);
        mk[rt]=0;
    }
}
 
int Solve(int x) {
    int y=mk[x];
    if(!y) return -2;
    Access(y);
    Splay(y);
    Node tmp=s[y];
    tmp=tmp+t[x];
    tmp.x=(1-tmp.x+P)%P;
    if(tmp.x==0) {
        if(tmp.y==0) return -2;
        if(tmp.y!=0) return -1;
    }
    tmp.y=tmp.y*Inv[tmp.x]%P;
    return tmp.y;
}
 
int Que(int x) {
    int rt=GetRoot(x);
    rt=Solve(rt);
    Access(x);
    Splay(x);
    Node t=s[x];
    if(t.x==0) return t.y;
    if(rt<0) return rt;
    return (t.x*rt+t.y)%P;
}
 
char opt[10];
 
int main(){
    Inv[0]=Inv[1]=1;
    rep(i,2,P-1) Inv[i]=(P-P/i)*Inv[P%i]%P;
    n=rd();
    rep(i,1,n) val[i].x=1;
    rep(i,1,n) {
        val[i].x=rd();
        int v=rd();
        val[i].y=rd();
        Link(i,v);
    }
    m=rd();
    rep(i,1,m) {
        scanf("%s",opt);
        if(opt[0]=='A') printf("%d\n",Que(rd()));
        else {
            int x=rd();
            Cut(x);
            val[x].x=rd();
            int v=rd();
            val[x].y=rd();
            Link(x,v);
        }
    }
}
 
 
 
 
 

HDU - 5467

建立状态树,在状态树上回溯更新

#include
#include
#include
#include
#include
#include
#include
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=3e5+10,P=1e9+7;


int n,m;
int fa[N],son[N][2],rev[N];
ll s[N],val[N];

inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][0]!=x&&son[fa[x]][1]!=x); }

void Up(int u) {
    if(!u) return;
    s[u]=val[u];
    if(son[u][0]) s[u]=s[u]*s[son[u][0]]%P;
    if(son[u][1]) s[u]=s[u]*s[son[u][1]]%P;
}
void Down(int u) {
    if(!u||!rev[u]) return;
    rep(i,0,1) {
        int t=son[u][i];
        if(!t) continue;
        rev[t]^=1;
        swap(son[t][0],son[t][1]);
    }
    rev[u]=0;
}

void rotate(int u) {
    int f=fa[u],ff=fa[f],d=dir(u);
    fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
    son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
    fa[f]=u,son[u][!d]=f;
    Up(f),Up(u),Up(ff);
}
void Splay(int u) {
    static int stk[N],top=0;
    int t=u;
    Up(u);
    while(!isroot(t)) stk[++top]=t,t=fa[t];
    stk[++top]=t;
    while(top) Down(stk[top--]);
    while(!isroot(u)) {
        int f=fa[u];
        if(!isroot(f)) {
            if(dir(u)^dir(f)) rotate(u);
            else rotate(f);
        }
        rotate(u);
    }
}

void Access(int x){ for(int t=0;x;t=x,x=fa[x]) Splay(x),son[x][1]=t,Up(x); }
int GetRoot(int x){
    Access(x),Splay(x);
    while(son[x][0]) Down(x),x=son[x][0];
    Splay(x);
    return x;
}
void MakeRoot(int x) {
    Access(x),Splay(x);
    rev[x]^=1;
    swap(son[x][0],son[x][1]);
}

int suc[N];
int Cut(int x,int y) {
    if(GetRoot(x)!=GetRoot(y)) return 0;
    MakeRoot(x),Access(y);
    Splay(x);
    if(son[x][1]!=y || fa[y]!=x || son[y][0]) return 0;
    son[x][1]=fa[y]=0,Up(x);
    return 1;
}
int Link(int x,int y) {
    if(GetRoot(x)==GetRoot(y)) return 0;
    MakeRoot(y);Splay(y);
    fa[y]=x;
    return 1;
}

ll Que(int x,int y) {
    if(GetRoot(x)!=GetRoot(y)) return 0;
    MakeRoot(x);
    Access(y);
    Splay(y);
    return s[y];
}


vector  G[N];
int opt[N],a[N],b[N];
ll Ans[N];
void dfs(int u) {
    if(opt[u]==1) suc[u]=Link(a[u],b[u]);
    if(opt[u]==2) suc[u]=Cut(a[u],b[u]);
    if(opt[u]==4) Ans[u]=Que(a[u],b[u]);
    if(opt[u]==5) {
        suc[u]=val[a[u]],val[a[u]]=b[u]*(b[u]-1)/2;
        Splay(a[u]);
    }
    rep(i,0,G[u].size()-1) dfs(G[u][i]);
    if(opt[u]==1 && suc[u]) Cut(a[u],b[u]);
    if(opt[u]==2 && suc[u]) Link(a[u],b[u]);
    if(opt[u]==5) {
        val[a[u]]=suc[u];
        Splay(a[u]);
    }
}

int main(){
    rep(kase,1,rd()) {
        n=rd(),m=rd();
        rep(i,1,n) {
            val[i]=rd(),val[i]=val[i]*(val[i]-1)/2;
            s[i]=val[i];
            fa[i]=son[i][0]=son[i][1]=rev[i]=0;
        }
        rep(i,0,m) G[i].clear();
        int now=0;
        rep(i,1,m) {
            opt[i]=rd();
            if(opt[i]==3) now=rd();
            else a[i]=rd(),b[i]=rd();
            G[now].push_back((int)i);
            now=i;
        }
        dfs(0);
        rep(i,1,m) if(opt[i]==4) printf("%lld\n",Ans[i]);
    }
}

\[ \ \]

\[ \ \]

子树查询

另开一个数组,存下树上其他儿子的总和,这个维护起来比较得**

因为在很多时候都要更新,所以细节贼多,不好口述

HDU - 5405

设所有点的总和\(Sum\)

将路径拉成一条链,就是求\(Sum^2-\)链上所有节点树儿子权值平方和​

#include
#include
#include
#include
#include
#include
#include
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=2e5+10,P=1e9+7;

int n,m;
ll a[N];
ll ts[N],s[N],qs[N],sqs[N];
int rev[N];
ll Sum;


struct Edge{
    int to,nxt;
} e[N<<1];
int head[N],ecnt;
void AddEdge(int u,int v) {
    e[++ecnt]=(Edge){v,head[u]};
    head[u]=ecnt;
}
#define erep(u,i) for(int i=head[u];i;i=e[i].nxt)

void Mod(ll &x,ll y) {
    x+=y;
    x=(x%P+P)%P;
}

int fa[N],son[N][2];

void pre_dfs(int u,int f) {
    fa[u]=f;
    qs[u]=ts[u]=0;
    son[u][0]=son[u][1]=0;
    erep(u,i) {
        int v=e[i].to;
        if(v==f) continue;
        pre_dfs(v,u);
    }
    s[u]=(a[u]+ts[u])%P;
    Mod(ts[f],s[u]);
    Mod(qs[f],s[u]*s[u]%P);
    sqs[u]=qs[u];
}

inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][1]!=x&&son[fa[x]][0]!=x); }

void Up(int u) {
    if(!u) return;
    s[u]=(ts[u]+a[u])%P;
    sqs[u]=qs[u];
    if(son[u][0]) Mod(s[u],s[son[u][0]]),Mod(sqs[u],sqs[son[u][0]]);
    if(son[u][1]) Mod(s[u],s[son[u][1]]),Mod(sqs[u],sqs[son[u][1]]);
}
void Down(int u) {
    if(!u || !rev[u]) return;
    int t;
    rep(i,0,1) {
        t=son[u][i];
        if(!t) continue;
        rev[t]^=1;
        swap(son[t][0],son[t][1]);
    }
    rev[u]=0;
}

void rotate(int u) {
    int f=fa[u],ff=fa[f],d=dir(u);
    fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
    son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
    fa[f]=u,son[u][!d]=f;
    Up(f),Up(u),Up(ff);
}
void Splay(int u) {
    static int stk[N],top=0;
    int t=u;
    while(!isroot(t)) stk[++top]=t,t=fa[t];
    stk[++top]=t;
    while(top) Down(stk[top--]);
    while(!isroot(u)) {
        int f=fa[u];
        if(!isroot(f)) {
            if(dir(u)^dir(f)) rotate(u);
            else rotate(f);
        }
        rotate(u);
    }
}

void Access(int x) {
    for(reg int t=x;t;t=fa[t]) Splay(t);
    for(reg int t=0,y=x;y;t=y,y=fa[y]) {
        if(t) {
            Mod(ts[y],-s[t]);
            Mod(qs[y],-s[t]*s[t]%P);
        }
    }
    for(reg int t=0;x;t=x,x=fa[x]) {
        Splay(x);
        if(son[x][1]) Mod(ts[x],s[son[x][1]]),Mod(qs[x],s[son[x][1]]*s[son[x][1]]%P);
        son[x][1]=t;
        Up(x);
    }
}
void MakeRoot(int x) {
    Access(x);
    Splay(x);
    rev[x]^=1;
    swap(son[x][0],son[x][1]);
}
void Upd(int x,int d) {
    Access(x);
    Splay(x);
    Sum-=a[x];
    Sum+=(a[x]=d);
    Up(x);
}

void Que(int x,int y){ 
    MakeRoot(x);
    Access(y); Splay(y);
    ll ans=(Sum%P*(Sum%P))%P-sqs[y];
    Mod(ans,0);
    printf("%lld\n",ans);
}

int main(){
    while(~scanf("%d%d",&n,&m)) {
        ecnt=Sum=0;
        rep(i,1,n) a[i]=rd(),head[i]=0,Sum+=a[i];
        rep(i,2,n) {
            int u=rd(),v=rd();
            AddEdge(u,v);
            AddEdge(v,u);
        }
        pre_dfs(1,0);
        rep(i,1,m) {
            int opt=rd();
            if(opt==1) {
                int x=rd(),w=rd();
                Upd(x,w);
            } else Que(rd(),rd());
        }
    }
}

你可能感兴趣的:(Link Cut Tree)