浅谈ETT

jerry3128

2021-04-26 13:12:33

Personal

Euler - Tour - Tree

Video Access: https://www.bilibili.com/video/BV1Hh8qeGEgt

零. 前言

本文着重介绍几类 ETT 的实现方式与其对应的维护信息,分析其在各种信息维护上的优劣。

  1. ETT 只是对能动态维护欧拉环游序数据结构的总称,其载体包括但不限于:平衡树、块链等数据结构。
  2. ET 序的存储方法也分为4种情况,待会会一一讨论。
  3. 介绍的顺序后面能做得操作包含前面能做的。

首先来介绍几种基于欧拉环游序和其的省略或者类似的版本。目前笔者自己研究的独立 ETT 仅能做到自由 link-cut 维护子树信息,要求交换律与结合律。

一. DFS序

二. 括号序

三. 欧拉环游序

下面就来介绍几种实用的例子。

一. splay - 括号序

二. FHQ - 正反弧ETT

三. LCT - ETT

例题

【Ynoi2012】 梦断 SCOI2017

题目大意:

题解:splay & 括号序。

//ayame保佑,夸哥保佑,狗妈保佑,MDR保佑,锉刀怪保佑,M99保佑,克爹保佑
#include<bits/stdc++.h>
using namespace std;
int p1=1000000,p2=0;
char buf[1000005],wb[1000005];
int gc() {
    if(p1>=1000000)fread(buf,1,1000000,stdin),p1=0;
    return buf[p1++];
}
#define gc getchar
#define Loli true
#define Kon xor true
long long getint() {
    long long ret=0,flag=1;
    char c=gc();
    while(c<'0'||c>'9') {
        if(c=='-')flag=-1;
        c=gc();
    }
    while(c<='9'&&c>='0') {
        ret=(ret<<3)+(ret<<1)+c-'0';
        c=gc();
    }
    return ret*flag;
}
void pc(char x) {
    if(p2>=1000000)fwrite(wb,1,1000000,stdout),p2=0;
    wb[p2++]=x;
}
void wrt(long long x) {
    if(x<0)pc('-'),x=-x;
    int c[24]= {0};
    if(!x)return pc('0'),void();
    while(x)c[++c[0]]=x%10,x/=10;
    while(c[0])pc(c[c[0]--]+'0');
}
int n,m,a[100005];
int fa[100005],dep[100005];
int o[100005][31],prt[20][100005];
namespace ETT {
    struct node {
        int ch[2],fa,pre,suf;
        int vl,sz,dep,mx,mi,etag,stag;
        int col,ctag;
    } v[200005];
    void push_down(int rt) {
        if(v[rt].ctag) {
            if(v[rt].ch[0])v[v[rt].ch[0]].ctag=v[v[rt].ch[0]].col=v[rt].ctag;
            if(v[rt].ch[1])v[v[rt].ch[1]].ctag=v[v[rt].ch[1]].col=v[rt].ctag;
            v[rt].ctag=0;
        }
    }
    void push_up(int rt) {
        v[rt].pre=v[rt].suf=rt;
        v[rt].mx=v[rt].mi=v[rt].dep;
        if(v[rt].ch[0])v[rt].pre=v[v[rt].ch[0]].pre,v[rt].mx=max(v[rt].mx,v[v[rt].ch[0]].mx),v[rt].mi=min(v[rt].mi,v[v[rt].ch[0]].mi);
        if(v[rt].ch[1])v[rt].suf=v[v[rt].ch[1]].suf,v[rt].mx=max(v[rt].mx,v[v[rt].ch[1]].mx),v[rt].mi=min(v[rt].mi,v[v[rt].ch[1]].mi);
        v[rt].stag=v[rt].etag|v[v[rt].ch[0]].stag|v[v[rt].ch[1]].stag;
        v[rt].sz=v[v[rt].ch[0]].sz+v[rt].vl+v[v[rt].ch[1]].sz;
    }
    void rot(int x) {
        int p=v[x].fa,g=v[p].fa;
        bool d=v[p].ch[1]==x;
        if(g)v[g].ch[v[g].ch[1]==p]=x;
        v[p].ch[d]=v[x].ch[d^1];
        v[v[x].ch[d^1]].fa=p;
        v[x].ch[d^1]=p;
        v[x].fa=g,v[p].fa=x;
        push_up(p),push_up(x);
    }
    void pre_push_down(int rt) {
        if(v[rt].fa)pre_push_down(v[rt].fa);
        push_down(rt);
    }
    void splay(int x,int f=0) {
        if(!x)return;
        pre_push_down(x);
        while(v[x].fa!=f) {
            int p=v[x].fa,g=v[p].fa;
            if(g!=f)rot(v[g].ch[0]==p^v[p].ch[0]==x?x:p);
            rot(x);
        }
    }
    void init() {
        for(int i=1; i<=n; i++) {
            v[i<<1].vl=1,v[i<<1].ch[1]=i<<1|1,v[i<<1|1].fa=i<<1,v[i<<1].col=a[i];
            v[i<<1].dep=v[i<<1|1].dep=dep[i],push_up(i<<1|1),push_up(i<<1);
        }
    }
    int merge_bro(int x,int y) {
        if(!x||!y)return x|y;
        splay(x),splay(y);
        int d=v[x].suf;
        splay(d),v[d].ch[1]=y,v[y].fa=d,push_up(d);
        return x;
    }
    void merge_prt(int x,int y) {
        splay(x<<1),splay(v[v[x<<1].ch[1]].pre,x<<1),splay(y);
        v[v[x<<1].ch[1]].ch[0]=y,v[y].fa=v[x<<1].ch[1],push_up(v[x<<1].ch[1]),push_up(x<<1);
    }
    int ask(int x) {
        if(x==0)return x;
        return splay(x),v[x].pre;
    }
    void dfs(int x,int c,vector<int>&vec) {
        push_down(x);
        if(v[x].etag>>c&1)vec.push_back(x>>1),v[x].etag^=1<<c;
        if(v[v[x].ch[0]].stag>>c&1)dfs(v[x].ch[0],c,vec);
        if(v[v[x].ch[1]].stag>>c&1)dfs(v[x].ch[1],c,vec);
        push_up(x);
    }
}
void ask(int x) {
    int p=x;
    for(int i=19; i>=0; i--)if(ETT::ask(p<<1)==ETT::ask(prt[i][p]<<1))p=prt[i][p];
    ETT::splay(p<<1),ETT::splay(p<<1|1,p<<1);
    int sz=ETT::v[ETT::v[p<<1|1].ch[0]].sz+1,len=max(ETT::v[ETT::v[p<<1|1].ch[0]].mx-dep[p],0)+1;
    wrt(ETT::v[p<<1].col),pc(' '),wrt(sz),pc(' '),wrt(len),pc('\n');
}
void paint_point(int x,int gc) {
    int p=x;
    for(int i=19; i>=0; i--)if(ETT::ask(p<<1)==ETT::ask(prt[i][p]<<1))p=prt[i][p];
    ETT::splay(x<<1),ETT::splay(x<<1|1,x<<1);
    int l=ETT::v[x<<1].ch[0],r=ETT::v[x<<1|1].ch[1],col=ETT::v[x<<1].col;
    ETT::v[l].fa=0,ETT::v[r].fa=0,ETT::v[x<<1].ch[0]=0,ETT::v[x<<1|1].ch[1]=0;
    ETT::push_up(x<<1|1),ETT::push_up(x<<1);

    if(fa[x]) {
        int tmp=ETT::merge_bro(l,r);
        o[fa[p]][col]=tmp;
        ETT::splay(fa[p]<<1);
        if(!o[fa[p]][col])ETT::v[fa[p]<<1].etag^=1<<col;
        ETT::push_up(fa[p]<<1);
    } else ETT::merge_bro(l,r);

    if(ETT::v[x<<1|1].ch[0]) {
        int d=ETT::v[x<<1|1].ch[0];
        ETT::v[d].fa=0,ETT::v[x<<1|1].ch[0]=0;
        ETT::push_up(x<<1|1),ETT::push_up(x<<1);
        o[x][col]=d,ETT::v[x<<1].etag|=1<<col,ETT::push_up(x<<1);
    }

    ETT::v[x<<1].col=ETT::v[x<<1|1].col=gc;

    if(o[x][gc]) {
        int d=o[x][gc];
        o[x][gc]=0,ETT::splay(d),ETT::v[x<<1|1].ch[0]=d,ETT::v[d].fa=x<<1|1;
        ETT::v[x<<1].etag^=1<<gc,ETT::push_up(x<<1|1),ETT::push_up(x<<1);
    }

    if(fa[x]) {
        ETT::splay(fa[x]<<1);
        if(gc!=ETT::v[fa[x]<<1].col) {
            o[fa[x]][gc]=ETT::merge_bro(o[fa[x]][gc],x<<1);
            ETT::splay(fa[x]<<1);
            ETT::v[fa[x]<<1].etag|=1<<gc;
            ETT::push_up(fa[x]<<1);
        } else ETT::merge_prt(fa[x],x<<1);
    }
}
void paint_block(int x,int gc) {
    for(int i=19; i>=0; i--)if(ETT::ask(x<<1)==ETT::ask(prt[i][x]<<1))x=prt[i][x];
    ETT::splay(x<<1),ETT::splay(x<<1|1,x<<1);
    int l=ETT::v[x<<1].ch[0],r=ETT::v[x<<1|1].ch[1],col=ETT::v[x<<1].col;
    ETT::v[l].fa=0,ETT::v[r].fa=0,ETT::v[x<<1].ch[0]=0,ETT::v[x<<1|1].ch[1]=0;
    ETT::push_up(x<<1|1),ETT::push_up(x<<1);

    if(fa[x]) {
        o[fa[x]][col]=ETT::merge_bro(l,r);
        ETT::splay(fa[x]<<1);
        if(!o[fa[x]][col])ETT::v[fa[x]<<1].etag^=1<<col;
        ETT::push_up(fa[x]<<1);
    } else ETT::merge_bro(l,r);

    ETT::v[x<<1].col=ETT::v[x<<1].ctag=gc;

    vector<int> vec;
    ETT::dfs(x<<1,gc,vec);
    for(int w:vec)ETT::merge_prt(w,o[w][gc]),o[w][gc]=0;

    if(fa[x]) {
        ETT::splay(fa[x]<<1);
        if(gc!=ETT::v[fa[x]<<1].col) {
            o[fa[x]][gc]=ETT::merge_bro(o[fa[x]][gc],x<<1);
            ETT::splay(fa[x]<<1);
            ETT::v[fa[x]<<1].etag|=1<<gc;
            ETT::push_up(fa[x]<<1);
        } else ETT::merge_prt(fa[x],x<<1);
    }
}
int main() {
    n=getint();
    for(int i=1; i<=n; i++)prt[0][i]=fa[i]=getint(),dep[i]=dep[fa[i]]+1;
    for(int j=1; j<20; j++)
        for(int i=1; i<=n; i++)prt[j][i]=prt[j-1][prt[j-1][i]];
    for(int i=1; i<=n; i++)a[i]=getint();
    ETT::init(),m=getint();
    for(int i=n; i>=2; i--) {
        if(a[fa[i]]==a[i])ETT::merge_prt(fa[i],i<<1);
        else o[fa[i]][a[i]]=ETT::merge_bro(o[fa[i]][a[i]],i<<1),ETT::splay(fa[i]<<1),ETT::v[fa[i]<<1].etag|=1<<a[i],ETT::push_up(fa[i]<<1);
    }
    for(int i=1; i<=m; i++) {
        int opt=getint();
        if(opt==1) {
            int x=getint(),c=getint();
            paint_point(x,c);
        }
        if(opt==2) {
            int x=getint(),c=getint();
            paint_block(x,c);
        }
        if(opt==3)ask(getint());
    }
    fwrite(wb,1,p2,stdout);
    return Loli Kon;
}

动态图的完全连通性与此类似,采用刚刚找边的均摊思想,但对 link - cut 的要求相对更强,用正反弧 ETT,和 HDLT 的分层图算法维护。