Euler-Tour Tree模板[bzoj 3786]及其講解

Euler-Tour-Tree

ETT即Euler-Tour-Tree,也就是什麽歐拉游覽樹
是一種可以維護子樹操作的動態樹
支持link,cut,單點修改,子樹修改,查詢點到根的信息
(爲什麽別的不行呢?因爲我不會,貌似ETT不支持換根,鏈操作什麽的)
怎麽做呢?
我們維護一棵樹的歐拉序
歐拉序是括號序列,一個點進棧時記錄一次dfn,出棧時再記錄一次dfn
就得到了一個有趣的序列
这里写图片描述
歐拉序有什麽有趣的性質呢?

子樹提取

當我們想去提取子樹信息是非常方便的
比如以2為根的子樹有2,3,4,5這些節點
而被2括起來的序列是2 3 3 4 5 5 4 2
所有的點都在其中,原因是歐拉序的定義,子樹沒有搜完不會讓自己再次加到dfn中
這爲ETT子樹修改提供了方便

查詢到根的信息

比如4到根的信息
我們在序列中可以找到這樣的一段1 2 3 3 4
由於3出現了兩次,説明從根到4的路徑上它出棧了,也就是説3不在這條鏈上
我們用差分的思想就可以解決,出棧點權=-入棧點權,就可以消去了
查詢到根的信息即查詢序列的區間信息
衹看以上兩個操作似乎衹需要綫段樹就可以解決了,但是顯然這樣算不上ETT,功能比不上樹鏈剖分
下面是動態樹的經典操作

link&cut

不能link和cut還叫什麽動態數
我們還需要歐拉序來搞事情
比如在這棵樹中
这里写图片描述
我想把4從2上cut下來,再與6link一下
我們生成這樣的一個序列就可以表示操作后的樹
这里写图片描述
我们可以直觀看到
link和cut的本質就是歐拉序列上一個子樹所代表序列的平移

Euler-Tour-Tree的實現

我們都有理論基礎了還怕什麽
區間修改,區間查詢,區間平移…
欽定的Splay Tree作爲輔助樹來維護歐拉序列
吼吼吼
上代碼(個人感覺比Link-Cut-Tree好理解,能滿足Link-Cut-Tree所不能做的操作,但是也有局限性)
注意維護為出棧點還是入棧點

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define MAXN 200010
using namespace std;
inline void read(int &x)
{
    int s=0,w=1;
    char c=getchar();
    while(!isdigit(c)){if(c=='-')w=-1;c=getchar();}
    while(isdigit(c)){s=(s<<3)+(s<<1)+c-'0';c=getchar();}
    x=s*w;
}
inline void write(long long x)
{
    if(x<0)x=-x,putchar('-');
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
struct edge
{
    int to,nxxt;
}e[MAXN];
int head[MAXN],cnt;
inline void add(int x,int y)
{
    e[++cnt].to=y;
    e[cnt].nxxt=head[x];
    head[x]=cnt;
}
int fa[MAXN],size[MAXN],son[MAXN][2],l[MAXN],r[MAXN],mark[MAXN],dfn[MAXN],tot,a[MAXN];
long long lazy[MAXN],sum[MAXN],val[MAXN];
inline void push_up(int root)
{
    sum[root]=sum[son[root][0]]+sum[son[root][1]]+val[root];
    size[root]=size[son[root][0]]+size[son[root][1]]+mark[root];
}
inline void push_down(int root)
{
    if(root&&fa[root])push_down(fa[root]);
    if(!root||!lazy[root])return ;
    lazy[son[root][0]]+=lazy[root],lazy[son[root][1]]+=lazy[root];
    val[son[root][0]]+=lazy[root]*mark[son[root][0]],val[son[root][1]]+=lazy[root]*mark[son[root][1]];
    sum[son[root][0]]+=(long long)size[son[root][0]]*lazy[root],sum[son[root][1]]+=(long long)size[son[root][1]]*lazy[root];
    lazy[root]=0;
}
inline int dir(int root)
{
    return son[fa[root]][1]==root;
}
inline void rotate(int root,int d)
{
    int temp=son[root][d^1];
    son[root][d^1]=son[temp][d];
    if(son[root][d^1])fa[son[root][d^1]]=root;
    fa[temp]=fa[root];
    if(fa[root])son[fa[root]][dir(root)]=temp;
    fa[root]=temp;
    son[temp][d]=root;
    push_up(root),push_up(temp);
}
inline void splay(int root,int goal)
{
    push_down(root);
    while(fa[root]!=goal)
    {
        if(fa[fa[root]]!=goal&&dir(root)==dir(fa[root]))
            rotate(fa[fa[root]],dir(root)^1);
        rotate(fa[root],dir(root)^1);
    }
}
inline int find_left(int x)
{
    splay(x,0);
    x=son[x][0];
    while(son[x][1])x=son[x][1];
    return x;
}
inline int find_right(int x)
{
    splay(x,0);
    x=son[x][1];
    while(son[x][0])x=son[x][0];
    return x;
}
int build(int left,int right)
{
    if(left>right)return 0;
    int mid=(left+right)>>1;
    if(mid<right)fa[son[mid][1]=build(mid+1,right)]=mid;
    if(left<mid)fa[son[mid][0]=build(left,mid-1)]=mid;
    val[mid]=(dfn[mid]>0)?a[dfn[mid]]:-a[-dfn[mid]];
    mark[mid]=(dfn[mid]>0)?1:-1;
    push_up(mid);
    return mid;
}
void dfs(int x)
{
    dfn[l[x]=++tot]=x;
    for(int i=head[x];i;i=e[i].nxxt)
        dfs(e[i].to);
    dfn[r[x]=++tot]=-x;
}
int n,x,y,m;
char opt;
int main()
{
    read(n);
    for(int i=2;i<=n;i++)
    {
        read(x);
        add(x,i);
    }
    dfs(1);
    for(int i=1;i<=n;i++)read(a[i]);
    build(1,tot+1);
    read(m);
    while(m--)
    {
        opt=getchar();
        while(opt!='Q'&&opt!='C'&&opt!='F')
            opt=getchar();
        read(x);
        if(opt=='Q')
        {
            splay(l[x],0);
            write(sum[son[l[x]][0]]+val[l[x]]),putchar(10);
        }
        else if(opt=='C')
        {
            read(y);
            int aa=find_left(l[x]),bb=find_right(r[x]);
            splay(aa,0),splay(bb,aa);
            int temp=son[bb][0];
            son[bb][0]=0;
            push_up(bb),push_up(aa);
            splay(x=find_left(r[y]),0),splay(r[y],x);
            fa[temp]=r[y];
            son[r[y]][0]=temp;
            push_up(r[y]),push_up(x);
        }
        else
        {
            read(y);
            int aa=find_left(l[x]),bb=find_right(r[x]);
            splay(aa,0),splay(bb,aa);
            lazy[son[bb][0]]+=y,val[son[bb][0]]+=y*mark[son[bb][0]],sum[son[bb][0]]+=(long long)size[son[bb][0]]*y;
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值