1. 程式人生 > >BZOJ3786星系探索——非旋轉treap(平衡樹動態維護dfs序)

BZOJ3786星系探索——非旋轉treap(平衡樹動態維護dfs序)

題目描述

物理學家小C的研究正遇到某個瓶頸。

他正在研究的是一個星系,這個星系中有n個星球,其中有一個主星球(方便起見我們預設其為1號星球),其餘的所有星球均有且僅有一個依賴星球。主星球沒有依賴星球。

我們定義依賴關係如下:若星球a的依賴星球是b,則有星球a依賴星球b.此外,依賴關係具有傳遞性,即若星球a依賴星球b,星球b依賴星球c,則有星球a依賴星球c.

對於這個神祕的星系中,小C初步探究了它的性質,發現星球之間的依賴關係是無環的。並且從星球a出發只能直接到達它的依賴星球b.

每個星球i都有一個能量係數wi.小C想進行若干次實驗,第i次實驗,他將從飛船上向星球di發射一個初始能量為0的能量收集器,能量收集器會從星球di開始前往主星球,並收集沿途每個星球的部分能量,收集能量的多少等於這個星球的能量係數。

但是星系的構成並不是一成不變的,某些時刻,星系可能由於某些複雜的原因發生變化。

有些時刻,某個星球能量激發,將使得所有依賴於它的星球以及他自己的能量係數均增加一個定值。還有可能在某些時刻,某個星球的依賴星球會發生變化,但變化後依然滿足依賴關係是無環的。

現在小C已經測定了時刻0時每個星球的能量係數,以及每個星球(除了主星球之外)的依賴星球。接下來的m個時刻,每個時刻都會發生一些事件。其中小C可能會進行若干次實驗,對於他的每一次實驗,請你告訴他這一次實驗能量收集器的最終能量是多少。

輸入

第一行一個整數n,表示星系的星球數。

接下來n-1行每行一個整數,分別表示星球2-n的依賴星球編號。

接下來一行n個整數,表示每個星球在時刻0時的初始能量係數wi.

接下來一行一個整數m,表示事件的總數。

事件分為以下三種類型。

(1)"Q di"表示小C要開始一次實驗,收集器的初始位置在星球di.

(2)"C xi yi"表示星球xi的依賴星球變為了星球yi.

(3)"F pi qi"表示星球pi能量激發,常數為qi.

輸出

對於每一個事件型別為Q的事件,輸出一行一個整數,表示此次實驗的收集器最終能量。

樣例輸入

3
1
1
4 5 7
5
Q 2
F 1 3
Q 2
C 2 3
Q 2

樣例輸出

9
15
25

提示

n<=100000,m<=300000,1<di,xi<=n,wi,qi<=100000.保證操作合法。注意w_i>=0

 

如果沒有遷移子樹操作考慮怎麼做?

求出原樹出棧入棧序(下文用dfs序代替),對於入棧點權值為正(即val[x]),對於出站點權值為負(即-val[x]),那麼詢問就是求詢問點在dfs序上入棧點的字首和(不在這個點到根路徑上的點出棧點和入棧點一定同時在詢問點的前邊,正負權值抵消)。

對於修改一定是dfs序上的一段區間,用線段樹區間修改即可,因為每個點可能是正權可能是負權,因此還要維護區間正權點數與負權點數之差。

那麼現在有了子樹遷移操作,dfs序是動態的了,顯然線段樹無法實現,那麼用平衡樹就好了。

將操作點子樹從treap中分裂出來然後插入到它新的父節點入棧點後面即可。

但因為這道題我們只知道dfs序上每個點對應平衡樹上節點的編號(建樹時開兩個陣列分別存一下),所以要再維護出平衡樹上每個點的父節點然後每次分裂前先從操作點往上找到根節點來得到這個點在平衡樹上的具體位置(即它現在是dfs序的第幾個點)再從根正常分裂。

這道題嚴重卡常,建議用上所有優化,蒟蒻的我差點沒卡過去QAQ。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long 
using namespace std;
char buf[100000],*p1,*p2;
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
__attribute__((optimize("-O3")))int rd() {
    register int x=0;register char ch=nc();
    while(ch<'0'||ch>'9') ch=nc();
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=nc();
    return x;
}
int n,m;
int x,y;
int tot;
int cnt;
int res;
int root;
int a,b,c;
int head[200010];
int to[200010];
int next[200010];
int num[200010];
int sig[200010];
ll sum[200010];
int v[200010];
int val[200010];
int pos[200010];
int size[200010];
int ls[200010];
int rs[200010];
int f[200010];
int r[200010];
int s[200010];
int t[200010];
int p[200010];
int q[200010];
inline void add(int x,int y)
{
    tot++;
    next[tot]=head[x];
    head[x]=tot;
    to[tot]=y;
}
inline int build(int val,int k)
{
    int rt=++cnt;
    r[rt]=rand();
    size[rt]=1;
    v[rt]=val*k;
    sum[rt]=1ll*v[rt];
    sig[rt]=k;
    num[rt]=sig[rt];
    return rt;
}
inline void pushup(int rt)
{
    size[rt]=size[ls[rt]]+size[rs[rt]]+1;
    num[rt]=sig[rt];
    sum[rt]=1ll*v[rt];
    if(ls[rt])
    {
        num[rt]+=num[ls[rt]];
        sum[rt]+=sum[ls[rt]];
    }
    if(rs[rt])
    {
        num[rt]+=num[rs[rt]];
        sum[rt]+=sum[rs[rt]];
    }
}
inline void pushdown(int rt)
{
    if(pos[rt])
    {
        pos[ls[rt]]+=pos[rt];
        pos[rs[rt]]+=pos[rt];
        sum[ls[rt]]+=1ll*num[ls[rt]]*pos[rt];
        sum[rs[rt]]+=1ll*num[rs[rt]]*pos[rt];
        v[ls[rt]]+=sig[ls[rt]]*pos[rt];
        v[rs[rt]]+=sig[rs[rt]]*pos[rt];
        pos[rt]=0;
    }
}
inline int merge(int x,int y)
{
    pushdown(x);
    pushdown(y);
    if(!x||!y)
    {
        return x+y;
    }
    if(r[x]<r[y])
    {
        rs[x]=merge(rs[x],y);
        if(rs[x])
        {
            f[rs[x]]=x;
        }
        pushup(x);
        return x;
    }
    else
    {
        ls[y]=merge(x,ls[y]);
        if(ls[y])
        {
            f[ls[y]]=y;
        }
        pushup(y);
        return y;
    }
}
inline void split(int rt,int &x,int &y,int k)
{
    pushdown(rt);
    if(!rt)
    {
        x=y=0;
        return ;
    }
    if(size[ls[rt]]>=k)
    {
        y=rt;
        split(ls[rt],x,ls[y],k);
        if(ls[y])
        {
            f[ls[y]]=y;
        }
        pushup(rt);
    }
    else
    {
        x=rt;
        split(rs[rt],rs[x],y,k-size[ls[rt]]-1);
        if(rs[x])
        {
            f[rs[x]]=x;
        }
        pushup(rt);
    }
}
inline int get_size(int rt)
{
    int ans=0;
    int flag=1;
    while(rt)
    {
        if(flag)
        {
            ans+=size[ls[rt]]+1;
        }
        flag=(rt==rs[f[rt]]);
        rt=f[rt];
    }
    return ans;
}
inline void dfs(int x)
{
    s[x]=build(val[x],1);
    root=merge(root,s[x]);
    for(int i=head[x];i;i=next[i])
    {
        dfs(to[i]);
    }
    t[x]=build(val[x],-1);
    root=merge(root,t[x]);
}
int main()
{
    srand(12378);
    n=rd();
    for(int i=2;i<=n;i++)
    {
        x=rd();
        add(x,i);
    }
    for(int i=1;i<=n;i++)
    {
        val[i]=rd();
    }
    dfs(1);
    m=rd();
    while(m--)
    {
        char op=nc();
        while(op!='C'&&op!='Q'&&op!='F')op=nc();
        int x=rd();
        if(op=='Q')
        {
            res=get_size(s[x]);
            split(root,a,b,res);
            f[a]=f[b]=0;
            printf("%lld\n",sum[a]);
            root=merge(a,b);
        }
        else if(op=='F')
        {
            y=rd();
            res=get_size(t[x]);
            split(root,b,c,res);
            f[b]=f[c]=0;
            res=get_size(s[x]);
            split(b,a,b,res-1);
            f[a]=f[b]=0;
            pos[b]+=y;
            sum[b]+=1ll*num[b]*y;
            v[b]+=sig[b]*y;
            root=merge(merge(a,b),c);
        }
        else
        {
            y=rd();
            res=get_size(t[x]);
            split(root,b,c,res);
            f[b]=f[c]=0;
            res=get_size(s[x]);
            split(b,a,b,res-1);
            f[a]=f[b]=0;
            root=merge(a,c);
            res=get_size(s[y]);
            split(root,a,c,res);
            f[a]=f[c]=0;
            root=merge(merge(a,b),c);
        }
    }
}