一棵樹,點有黑白。稱某個點被最近的黑點祖先所“管轄”。管轄其它點的點稱為“管轄點”。
支持如下操作:
- 詢問\(x\)的權值。
- 給\(x\)所管轄的點加權值。
- 詢問\(x\)的子樹的權值和。
- 給\(x\)的子樹中所有的管轄點做操作。
- 把\(x\)變黑。
- 把\(x\)變白。
\(n\le 3*10^5,Q\le 5*10^5\)
比賽時為什么沒有搞出來……
為了方便記\(x\)的管轄點為\(y\)。
由於修改操作太麻煩,不妨考慮操作2只在\(y\)處加權值,操作1找\(y\)的權值,操作3求\(x\)子樹中的管轄點的權值和乘管轄范圍,另外加上\(y\)的權值乘\(x\)子樹中包含\(x\)的白色聯通塊的大小,操作4區間改管轄點的權值。這樣在沒有5和6操作時可以隨便做。
現在支持5和6操作。5操作時,找出\(y\)的權值,插入新點,並將新點的權值設為\(y\)的權值;6操作時,刪除之后,給\(x\)的子樹整體加\(w_x-w_y\),再用4操作給\(x\)中所有管轄點權值加\(-(w_x-w_y)\)。\(w\)為權值。
操作的時候有些小操作,這里就不說了。
具體的實現的時候,搞三個數據結構:
- 用樹剖+set,可以做到\(O(\lg n)\)地找到每個點對應的管轄點。
- 用樹狀數組,維護給子樹整體加的操作。
- 用線段樹,維護形如\(\sum c_iw_i\)的序列,支持查區間查\(\sum c_iw_i\),區間查\(\sum c_i\),單點查\(w_i\),單點改\(c_i\),區間改\(w_i\)(只改\(c_i>0\)的部分)。不知道樹狀數組行不行呢。
時間復雜度\(O(n\lg n)\)。
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#define N 300005
#define ui unsigned
#define INF 1000000000
int n,m;
int fa[N];
struct EDGE{
int to;
EDGE *las;
} e[N];
int ne;
EDGE *last[N];
int dep[N],siz[N],hs[N],top[N],in[N],out[N],nowdfn;
int mn[N];
struct cmp{bool operator()(int a,int b){return dep[a]<dep[b];}};
set<int,cmp> b[N];
void updmn(int x){
mn[x]=(b[x].empty()?INF:dep[*b[x].begin()]);
}
void init1(int x){
in[x]=++nowdfn;
siz[x]=1;
for (EDGE *ei=last[x];ei;ei=ei->las){
dep[ei->to]=dep[x]+1;
init1(ei->to);
siz[x]+=siz[ei->to];
if (siz[ei->to]>siz[hs[x]])
hs[x]=ei->to;
}
out[x]=nowdfn;
}
void init2(int x,int t){
top[x]=t;
if (hs[x]){
init2(hs[x],t);
for (EDGE *ei=last[x];ei;ei=ei->las)
if (ei->to!=hs[x])
init2(ei->to,ei->to);
}
}
int find(int x){
for (;x;x=fa[top[x]])
if (mn[top[x]]<=dep[x]){
auto p=b[top[x]].upper_bound(x);
--p;
return *p;
}
}
struct TA{
ui t[N];
void add(int x,ui c){
for (;x<=n;x+=x&-x)
t[x]+=c;
}
ui query(int x){
ui r=0;
for (;x;x-=x&-x)
r+=t[x];
return r;
}
};
struct TA2{
TA t0,t1;
void add(int l,int r,ui c){
t1.add(l,c),t1.add(r+1,-c);
t0.add(l,-c*(l-1)),t0.add(r+1,c*r);
}
ui query(int l,int r){
ui s=0;
s+=t0.query(r)+t1.query(r)*(ui)r;
s-=t0.query(l-1)+t1.query(l-1)*(ui)(l-1);
return s;
}
} s;
ui c[N*4],w[N*4],cw[N*4];
void gt(int k,ui v){
if (c[k]){
cw[k]+=c[k]*v;
w[k]+=v;
}
}
void upd(int k){
c[k]=c[k<<1]+c[k<<1|1];
cw[k]=cw[k<<1]+cw[k<<1|1];
}
void pd(int k){
gt(k<<1,w[k]);
gt(k<<1|1,w[k]);
w[k]=0;
}
ui sum(ui q[],int st,int en,int k=1,int l=1,int r=n){
if (st<=l && r<=en)
return q[k];
pd(k);
ui res=0;
int mid=l+r>>1;
if (st<=mid) res+=sum(q,st,en,k<<1,l,mid);
if (mid<en) res+=sum(q,st,en,k<<1|1,mid+1,r);
return res;
}
void addc(int x,ui v,int k=1,int l=1,int r=n){
if (l==r){
c[k]+=v;
if (!c[k]) w[k]=0;
cw[k]=c[k]*w[k];
return;
}
pd(k);
int mid=l+r>>1;
if (x<=mid) addc(x,v,k<<1,l,mid);
else addc(x,v,k<<1|1,mid+1,r);
upd(k);
}
void addw(int st,int en,ui v,int k=1,int l=1,int r=n){
if (st<=l && r<=en){
gt(k,v);
return;
}
pd(k);
int mid=l+r>>1;
if (st<=mid) addw(st,en,v,k<<1,l,mid);
if (mid<en) addw(st,en,v,k<<1|1,mid+1,r);
upd(k);
}
ui qw(int x){return sum(w,in[x],in[x]);}
ui qc(int x){return sum(c,in[x],in[x]);}
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
freopen("pastel.in","r",stdin);
freopen("pastel.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=2;i<=n;++i){
scanf("%d",&fa[i]);
e[ne]={i,last[fa[i]]};
last[fa[i]]=e+ne++;
}
init1(1);
init2(1,1);
for (int i=1;i<=n;++i)
mn[i]=INF;
b[1].insert(1);
updmn(1);
addc(in[1],siz[1]);
while (m--){
int op,x;
scanf("%d%d",&op,&x);
if (op==1){
int y=find(x);
printf("%u\n",qw(y)+s.query(in[x],in[x]));
}
else if (op==3){
int y=find(x);
ui ans=sum(cw,in[x],out[x])+(siz[x]-sum(c,in[x],out[x]))*qw(y);
ans+=s.query(in[x],out[x]);
printf("%u\n",ans);
}
else if (op==5){
int y=find(x);
ui w=qw(y),v=siz[x]-sum(c,in[x],out[x]);
addc(in[y],-v);
addc(in[x],v),addw(in[x],in[x],w);
b[top[x]].insert(x);
updmn(top[x]);
}
else if (op==6){
int y=find(fa[x]);
ui w=qw(x),v=qc(x);
addc(in[y],+v);
addc(in[x],-v);
// addw(in[x],in[x],-w);
w=w-qw(y);
s.add(in[x],out[x],w);
addw(in[x],out[x],-w);
b[top[x]].erase(x);
updmn(top[x]);
}
else{
ui v;
scanf("%u",&v);
if (op==2)
addw(in[x],in[x],v);
else
addw(in[x],out[x],v);
}
}
return 0;
}