\(\frak{Description}\)

\(\rm Link.\)

\(\frak{Solution}\)

据说是伪 \(\text{ett}\). 由于笔者根本不知道 \(\rm ett\) 是啥,所以把它称作 —— 欧拉序 + 平衡树。

具体怎么做呢?我们设点 \(u\) 的进入,出去的时间戳为 \(st_u,ed_u\). 然后在欧拉序序列上建一棵平衡树,给 \(st_u\) 的权值赋为 \(val_u\),给 \(ed_u\) 的权值赋为 \(-val_u\).

  • \(\text Q\):就是查询点 \(d\) 到点 \(1\) 的权值和,我们把它转化成求 \([st_1,st_d]\) 的权值和。可以看作在栈里加点,弹点(遇 \(st\) 加入,遇 \(ed\) 弹出),实际上栈里的点就是 \(1,d\) 的路径。我们给 \(st,ed\) 赋值相反数的操作其实是为了模拟弹栈。不过我们不能查询路径权值和,比如下面这张图:

    计算 \((x,y)\) 之间的路径,显然上行路径会计算成负数。不过如果可以动态维护 \(\rm lca\),我们就可以转化成两条下行路径,就可以询问辣!可是笔者只会 \(\rm lct\) 动态维护 \(\rm lca\)!所以为啥不直接写 \(\rm lct\) 呢😆?不过对于 \(f(f(x))=x\) 的操作,比如说异或,就可以用路径查询(

  • \(\text C\):其实是把以 \(x\) 为根的子树变成 \(y\) 的儿子。因为表示 \(x\) 为根子树的欧拉序序列是连续且为 \([st_x,ed_x]\),我们可以把这段区间移到 \(st_y\) 的后面,这个用平衡树维护。

  • \(\text F\):对于 \([st_p,ed_p]\) 之内的节点,\(st\) 加上 \(q\)\(ed\) 减去 \(q\).

详见代码。

\(\frak{Code}\)

#include <cstdio>

#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}

typedef long long ll;

const int maxn=1e5+5;

int head[maxn],nxt[maxn],to[maxn],cnt,n,m,idx=1,Val[maxn<<1],l[maxn],r[maxn],a[maxn];
char str[10];
struct Splay {
    int rt;
    struct node {
        int pos,neg,val,son[2],op,f;
        ll sum,tag;
        // pos 与 neg:自己为根的子树有多少个 st/ed,这个用来算加 k 值时的正负。
    } t[maxn<<1];

    bool dir(int x) {return t[t[x].f].son[1]==x;}

    void add(int x,int fa,bool dir) {
        if(x) t[x].f=fa;
        if(fa) t[fa].son[dir]=x;
    }

    void upd(int o,int k) {
        if(!o) return;
        t[o].tag+=k;
        t[o].sum+=1ll*k*(t[o].pos-t[o].neg);
        t[o].val+=1ll*k*t[o].op;
    }

    void pushDown(int o) {
        if(!o||(!t[o].tag)) return;
        upd(t[o].son[0],t[o].tag),upd(t[o].son[1],t[o].tag);
        t[o].tag=0;
    }

    void pushUp(int o) {
        if(!o) return;
        t[o].sum=t[t[o].son[0]].sum+t[t[o].son[1]].sum+t[o].val;
        t[o].pos=t[t[o].son[0]].pos+t[t[o].son[1]].pos+(t[o].op==1);
        t[o].neg=t[t[o].son[0]].neg+t[t[o].son[1]].neg+(t[o].op==-1);
    }

    void rotate(int x) {
        int fa=t[x].f,ffa=t[fa].f,dx=dir(x),df=dir(fa);
        pushDown(fa),pushDown(x);
        add(t[x].son[dx^1],fa,dx);
        add(fa,x,dx^1);
        add(x,ffa,df);
        pushUp(fa),pushUp(x);
    }

    void splay(int x,int goal) {
        for(int fa;(fa=t[x].f)^goal;rotate(x))
            if(t[fa].f^goal) rotate(dir(x)==dir(fa)?fa:x);
        if(!goal) rt=x;
    }

    int pre(int x) {x=t[x].son[0]; while(t[x].son[1]) x=t[x].son[1]; return x;}

    int suf(int x) {x=t[x].son[1]; while(t[x].son[0]) x=t[x].son[0]; return x;}

    void Build(int &o,int l,int r,int fa) {
        o=l+r>>1; t[o].f=fa;
        if(Val[o]>0) t[o].op=1,t[o].val=Val[o]-1;
        else t[o].op=-1,t[o].val=Val[o]+1;
        if(l<o) Build(t[o].son[0],l,o-1,o);
        if(r>o) Build(t[o].son[1],o+1,r,o);
        pushUp(o);
    }

    ll Query(int u) {
        int x,y; 
        splay(l[1],0),x=pre(l[1]);
        splay(l[u],0),y=suf(l[u]);
        splay(x,0),splay(y,x);
        return t[t[y].son[0]].sum;
    }

    void Add(int u,int k) {
        int x,y;
        splay(l[u],0),x=pre(l[u]);
        splay(r[u],0),y=suf(r[u]);
        splay(x,0),splay(y,x);
        upd(t[y].son[0],k),pushDown(t[y].son[0]);
    }

    void Modify(int u,int v) {
        int x,y;
        splay(l[u],0),x=pre(l[u]);
        splay(r[u],0),y=suf(r[u]);
        splay(x,0),splay(y,x);
        int Son=t[y].son[0]; t[Son].f=t[y].son[0]=0; pushUp(y);
        splay(l[v],0);
        int nxt=suf(l[v]); splay(nxt,l[v]);
        t[Son].f=nxt,t[nxt].son[0]=Son;
        pushUp(Son),pushUp(nxt);
    }
} T;

void addEdge(int u,int v) {
    nxt[++cnt]=head[u],to[cnt]=v,head[u]=cnt;
}

void dfs(int u) {
    l[u]=++idx,Val[idx]=a[u]+1; 
    erep(i,u) dfs(to[i]);
    r[u]=++idx,Val[idx]=-a[u]-1; 
}

int main() {
    int x,y;
    n=read(9);
    rep(i,2,n) x=read(9),addEdge(x,i);
    rep(i,1,n) a[i]=read(9);
    dfs(1);
    T.Build(T.rt,1,idx+1,0);
    for(int m=read(9);m;--m) {
        scanf("%s",str); x=read(9);
        if(str[0]=='Q') print(T.Query(x),'\n');
        else if(str[0]=='C') y=read(9),T.Modify(x,y);
        else y=read(9),T.Add(x,y);
    }
    return 0;
}
posted on 2021-01-06 20:57  Oxide  阅读(88)  评论(0编辑  收藏  举报