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;
}
}
}