6869. 【2020.11.17提高组模拟】鼠树
一棵树,点有黑白。称某个点被最近的黑点祖先所“管辖”。管辖其它点的点称为“管辖点”。
支持如下操作:
- 询问\(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;
}