bzoj3786 星系探索(ETT)

ETT就是Euler Tour Tree,用平衡树来维护欧拉序。
而欧拉序可以写成很多形式:
1.The first way is to write down all edges of the tree, directed, in order of DFS. This is how ETT is defined on
2.The second way is to store vertices. Each vertex is added to the array twice: when we descend into it and when we leave it.
3.The third way implies storing vertices too, but now each vertex is added every time when we visit it (when descending from parent and when returning from child)
第一种就是存dfs顺序的有向边,是欧拉遍序的官方定义,不过基本不用。
第二种就是常用的dfs序,括号序,这样每一个子树的点都是一段连续的区间,可以在进去的时候给正权,出来的时候给负权,这样直接加和就是答案了。
第三种就是rmq O(1)求lca经常用到的那个序。

而本题我们用的是第二种。
看一看用ETT我们可以做什么?
我们可以查询根到点的点权和(就是in[x]的前缀和)
我们可以删一棵子树(把对应区间删掉,可以用splay方便的提取区间实现)
我们可以加一棵子树(插入一个区间)
我们可以子树操作。
我们可以求子树和。
我们可以做链操作(求个lca然后分别到根容斥一下?需要可减?)
听说还可以换根?不过看上去巨麻烦x(貌似需要放到lct上,然后多个log,然后就可以实现和top tree同样的功能?)

一些参考资料:国家集训队论文集2014年《浅谈动态树的相关问题及简单拓展》
http://memphis.is-programmer.com/2015/8/7/linkcutmemphis.99293.html

我好像连splay都不会打了qaq

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 100010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(T==S){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
inline char get_S(){
    char ch=gc();
    while(ch<'A'||ch>'Z') ch=gc();return ch;
}
int n,fa[N<<1],c[N<<1][2],sz[N<<1],in[N],out[N],dfn=0,w[N],sig[N<<1],rt=0,q[N<<1];
ll v[N<<1],sum[N<<1],tag[N<<1];
vector<int>Son[N];
void dfs(int x){
    in[x]=++dfn;v[dfn]=w[x];sig[dfn]=1;
    for(int i=0;i<Son[x].size();++i) dfs(Son[x][i]);
    out[x]=++dfn;v[dfn]=-w[x];sig[dfn]=-1;
}
inline void update(int p){
    int l=c[p][0],r=c[p][1];
    sz[p]=sz[l]+sz[r]+sig[p];
    sum[p]=sum[l]+sum[r]+v[p];
}
inline void doadd(int p,ll val){
    tag[p]+=val;v[p]+=val*sig[p];sum[p]+=(ll)val*sz[p];
}
inline void pushdown(int p){
    if(!tag[p]) return;
    int l=c[p][0],r=c[p][1];
    if(l) doadd(l,tag[p]);if(r) doadd(r,tag[p]);tag[p]=0;
}
inline void build(int &p,int l,int r){
    int mid=l+r>>1;p=mid;tag[p]=0;
    if(l<mid) build(c[p][0],l,mid-1),fa[c[p][0]]=p;
    if(r>mid) build(c[p][1],mid+1,r),fa[c[p][1]]=p;update(p);
}
inline void rotate(int x,int &k){
    int y=fa[x],z=fa[y],l=x==c[y][1],r=l^1;
    if(y==k) k=x;
    else c[z][y==c[z][1]]=x;
    fa[c[x][r]]=y;fa[y]=x;fa[x]=z;
    c[y][l]=c[x][r];c[x][r]=y;update(y);update(x);
}
inline void splay(int x,int &k){
    int top=0;q[++top]=x;
    for(int xx=x;xx!=k;xx=fa[xx]) q[++top]=fa[xx];
    while(top) pushdown(q[top--]);
    while(x!=k){
        int y=fa[x],z=fa[y];
        if(y!=k){
            if(x==c[y][1]^y==c[z][1]) rotate(x,k);
            else rotate(y,k);
        }rotate(x,k);
    }
}
inline int findpre(int x){
    splay(x,rt);int res=c[x][0];
    while(c[res][1]) res=c[res][1];
    return res;
}
inline int findsucc(int x){
    splay(x,rt);int res=c[x][1];
    while(c[res][0]) res=c[res][0];
    return res;
}
inline int split(int xx,int yy){
    int x=findpre(xx),y=findsucc(yy);
    splay(x,rt);splay(y,c[rt][1]);return c[y][0];
}
inline int del1(int l,int r){
    int x=split(l,r),y=fa[x];
    c[y][0]=fa[x]=0;update(y);update(fa[y]);return x;
}inline void add(int l,int r,int val){
    int x=split(l,r),y=fa[x];
    doadd(x,val);update(y);update(fa[y]);
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();dfn=1;
    for(int i=2;i<=n;++i) Son[read()].push_back(i);
    for(int i=1;i<=n;++i) w[i]=read();dfs(1);
    build(rt,1,n*2+2);int m=read();
    while(m--){
        char op=get_S();int x=read();
        if(op=='Q'){
            splay(in[x],rt);
            printf("%lld\n",sum[c[in[x]][0]]+v[in[x]]);continue;
        }int y=read();
        if(op=='C'){
            x=del1(in[x],out[x]);
            int xx=findpre(out[y]),yy=out[y];
            splay(xx,rt);splay(yy,c[rt][1]);c[yy][0]=x;fa[x]=yy;update(yy);update(xx);
        }else add(in[x],out[x],y);
    }return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值