【BZOJ】3786: 星系探索

【题意】给定一棵带点权树,三种操作:

1.询问点x到根的路径和

2.子树x内的点权加定值y

3.将点x的父亲更换为y,保证仍是树。

【算法】平衡树(fhq-treap)

【题解】

将树的dfs序作为序列维护,对每个点入栈+1,出栈-1,这样操作1就是前缀和(非此路径的都会正负抵消),操作2就是区间加值,操作3就是区间移动,可以用平衡树维护。

具体实现:原树上每个点在序列中对应两个点(入栈和出栈),每个点维护自身系数(1或-1),自身数值(含系数),系数和,数值和。维护系数是为了满足通过标记直接修改sum。

还有一个问题,只知道点的编号如何查询点的排名,实际上就是左子树+往上左走时的所有左子树(均含本点)。

过程中,在up处更新左右节点的父亲即可,但要额外更新分裂合并时根节点的父亲。

记得开long long。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=400010;
struct cyc{int l,r,rnd,sz,fa,gnum,gsum;ll delta,num,sum;}t[maxn];
struct edge{int v,from;}e[maxn];
int tote=0,first[maxn];
void insert(int u,int v){tote++;e[tote].v=v;e[tote].from=first[u];first[u]=tote;}
int n,m,be[maxn],ed[maxn],tot,a[maxn],b[maxn],c[maxn],st[maxn],root;
int read(){
    char c;int s=0,t=1;
    while(!isdigit(c=getchar()))if(c=='-')t=-1;
    do{s=s*10+c-'0';}while(isdigit(c=getchar()));
    return s*t;
}
void dfs(int x){
    be[x]=++tot;b[tot]=1;c[tot]=a[x];
    for(int i=first[x];i;i=e[i].from){
        dfs(e[i].v);
    }
    ed[x]=++tot;b[tot]=-1;c[tot]=-a[x];
}
void up(int k){
    t[k].sz=1+t[t[k].l].sz+t[t[k].r].sz;
    t[k].gsum=t[k].gnum+t[t[k].l].gsum+t[t[k].r].gsum;
    t[k].sum=t[k].num+t[t[k].l].sum+t[t[k].r].sum;
    if(t[k].l)t[t[k].l].fa=k;
    if(t[k].r)t[t[k].r].fa=k;
}
void modify(int k,int x){t[k].num+=1ll*t[k].gnum*x;t[k].sum+=1ll*t[k].gsum*x;t[k].delta+=x;}
void down(int k){
    if(t[k].delta){
        modify(t[k].l,t[k].delta);modify(t[k].r,t[k].delta);
        t[k].delta=0;
    }
}
void DFS(int k){
    if(!k)return;
    DFS(t[k].l);DFS(t[k].r);
    up(k);
}
void build(){
    int top=0;
    for(int i=1;i<=tot;i++){
        t[i]=(cyc){0,0,rand(),1,0,b[i],b[i],0,c[i],c[i]};
        while(top&&t[st[top]].rnd>t[i].rnd){
            t[st[top]].r=t[i].l;
            t[i].l=st[top--];
        }
        t[st[top]].r=i;
        st[++top]=i;
    }
    t[0]=(cyc){0,0,0,0,0,0,0,0,0};
    DFS(root=st[1]);
    t[root].fa=0;
}
int find(int x){
    int sum=t[t[x].l].sz+1;
    while(t[x].fa!=0){
        if(t[t[x].fa].r==x)sum+=t[t[t[x].fa].l].sz+1;
        x=t[x].fa;
    }
    return sum;
}
int merge(int a,int b){
    if(!a||!b)return a^b;
    if(t[a].rnd<t[b].rnd){
        down(a);
        t[a].r=merge(t[a].r,b);
        up(a);
        return a;
    }
    else{
        down(b);
        t[b].l=merge(a,t[b].l);
        up(b);
        return b;
    }
}
void split(int k,int &l,int &r,int x){
    if(!k)return void(l=r=0);
    down(k);
    if(x<t[t[k].l].sz+1){
        r=k;
        split(t[k].l,l,t[k].l,x);
    }
    else{
        l=k;
        split(t[k].r,t[k].r,r,x-t[t[k].l].sz-1);
    }
    up(k);
}
ll goroot(int x){
    int a,b;
    split(root,a,b,find(be[x]));
    ll ans=t[a].sum;
    root=merge(a,b);
    t[root].fa=0;
    return ans;
}
void change(int x,int y){
    int a,b,c;
    split(root,b,c,find(ed[x]));t[b].fa=t[c].fa=0;
    split(b,a,b,find(be[x])-1);t[a].fa=t[b].fa=0;
    modify(b,y);
    root=merge(a,b);
    root=merge(root,c);
    t[root].fa=0;
}
void move(int x,int y){
    int a,b,c;
    split(root,b,c,find(ed[x]));t[b].fa=t[c].fa=0;
    split(b,a,b,find(be[x])-1);t[a].fa=t[b].fa=0;
    root=merge(a,c);
    t[root].fa=0;
    split(root,a,c,find(be[y]));t[a].fa=t[c].fa=0;
    root=merge(a,b);
    root=merge(root,c);
    t[root].fa=0;
}
char s[10];
int main(){
    srand(233);
    n=read();
    for(int i=2;i<=n;i++)insert(read(),i);
    for(int i=1;i<=n;i++)a[i]=read();
    dfs(1);build();
    m=read();
    for(int i=1;i<=m;i++){
        scanf("%s",s);
        int x=read();
        if(s[0]=='Q'){
            printf("%lld\n",goroot(x));
        }
        if(s[0]=='C'){
            int y=read();
            move(x,y);
        }
        if(s[0]=='F'){
            int y=read();
            change(x,y);
        }
    }
    return 0;
}
View Code

 

posted @ 2017-12-03 22:12  ONION_CYC  阅读(206)  评论(0编辑  收藏