描述
给定一棵 n 个节点的树,初始时该树的根为 1 号节点,每个节点有一个给定的权值。下面依次进行 m 个操作,操作分为如下五种类型:
换根:将一个指定的节点设置为树的新根。
修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。
修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。
询问路径:询问某条路径上节点的权值和。
询问子树:询问某个子树内节点的权值和。
输入
第一行为一个整数 n,表示节点的个数。
第二行 n 个整数表示第$ i $个节点的初始权值 a i a_i ai
第三行 n − 1 n−1 n−1 个整数,表示 i + 1 i+1 i+1 号节点的父节点编号 f i + 1 ( 1 ⩽ f i + 1 ⩽ n ) f_{i+1}\ (1 \leqslant f_{i+1} \leqslant n) fi+1 (1⩽fi+1⩽n)
第四行一个整数 m m m,表示操作个数。
接下来 m m m 行,每行第一个整数表示操作类型编号: ( 1 ⩽ u , v ⩽ n ) (1 \leqslant u, v \leqslant n) (1⩽u,v⩽n)
若类型为 1 1 1,则接下来一个整数 u u u,表示新根的编号。
若类型为 2 2 2,则接下来三个整数 u , v , k u,v,k u,v,k,分别表示路径两端的节点编号以及增加的权值。
若类型为 3 3 3,则接下来两个整数 u , k u,k u,k,分别表示子树根节点编号以及增加的权值。
若类型为 4 4 4,则接下来两个整数 u , v u,v u,v,表示路径两端的节点编号。
若类型为 5 5 5,则接下来一个整数 u u u,表示子树根节点编号。
输出
对于每一个类型为 4 或 5 的操作,输出一行一个整数表示答案。
样例输入
6
1 2 3 4 5 6
1 2 1 4 4
6
4 5 6
2 2 4 1
5 1
1 4
3 1 2
4 2 5
样例输出
15
24
19
提示
对于 100 % 100\% 100% 的数据, 1 ⩽ n , m , k , a i ⩽ 1 0 5 1\leqslant n,m,k,a_i\leqslant 10^5 1⩽n,m,k,ai⩽105。数据有一定梯度。
解析:
其实说是换根,并不会真正的换根,只是换一个根标记的位置,不然怎么维护原来轻重链剖分的优秀结构。
思路:
改变根对路径的形态显然没有影响。
考虑怎么计算出根改变后不同节点的当前子树。
考虑我们现在的树根是
r
o
o
t
root
root,当前询问的子树根节点是
u
u
u。
那么会有以下情况(以下
L
C
A
LCA
LCA均指原树中的
L
C
A
LCA
LCA)
1.
u
=
=
r
o
o
t
u==root
u==root,那么
u
u
u的子树就是整棵树。
2.
L
C
A
(
r
o
o
t
,
u
)
≠
u
LCA(root,u)\neq u
LCA(root,u)̸=u,即
r
o
o
t
root
root不在
u
u
u的子树中。那么
u
u
u现在的子树就是原来的子树
3.
L
C
A
(
r
o
o
t
,
u
)
=
u
LCA(root,u)=u
LCA(root,u)=u,即
u
u
u在原来的树中是
r
o
o
t
root
root的祖先。那么我们找到
u
u
u到
r
o
o
t
root
root路径上的第一个儿子。这个儿子对应的原树中的子树,就是现在
u
u
u的子树的补集。
分别对应转换一下就好了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline int getint(){
re int num;
re char c;
re bool f=0;
while(!isdigit(c=gc()))if(c=='-')f=1;num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return f?-num:num;
}
inline void outint(ll a){
static char ch[23];
if(a==0)pc('0');
if(a<0)pc('-'),a=-a;
while(a)ch[++ch[0]]=a-a/10*10,a/=10;
while(ch[0])pc(ch[ch[0]--]^48);
}
cs int N=100005;
int last[N],nxt[N<<1],to[N<<1],ecnt;
inline void addedge(int u,int v){
nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v;
nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u;
}
int son[N],siz[N],fa[N],top[N],dep[N],val[N];
void dfs1(int u){
siz[u]=1;
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
if(v==fa[u])continue;
fa[v]=u;
dep[v]=dep[u]+1;
dfs1(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
int in[N],out[N],tot,pos[N];
void dfs2(int u){
pos[in[u]=++tot]=u;
if(son[u]){
top[son[u]]=top[u];
dfs2(son[u]);
}
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
if(v==fa[u]||v==son[u])continue;
top[v]=v;
dfs2(v);
}
out[u]=tot;
}
inline int LCA(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]>dep[top[v]])swap(u,v);
v=fa[top[v]];
}
return dep[u]>dep[v]?v:u;
}
int root;
inline void tree_dissection(){
dfs1(root=1);
top[1]=1;
dfs2(1);
}
ll sum[N<<2],add[N<<2];
inline void pushup(int k){
sum[k]=sum[k<<1]+sum[k<<1|1];
}
inline void pushnow(int k,int l,int r,int val){
add[k]+=val;
sum[k]+=1ll*(r-l+1)*val;
}
inline void pushdown(int k,int l,int r){
if(add[k]){
int mid=(l+r)>>1;
pushnow(k<<1,l,mid,add[k]);
pushnow(k<<1|1,mid+1,r,add[k]);
add[k]=0;
}
}
void build(int k,int l,int r){
if(l==r){
sum[k]=val[pos[l]];
return ;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
ll query(int k,int l,int r,cs int &ql, cs int &qr){
if(ql<=l&&r<=qr)return sum[k];
pushdown(k,l,r);
int mid=(l+r)>>1;
if(qr<=mid)return query(k<<1,l,mid,ql,qr);
if(mid<ql)return query(k<<1|1,mid+1,r,ql,qr);
return query(k<<1,l,mid,ql,qr)+query(k<<1|1,mid+1,r,ql,qr);
}
void modify(int k,int l,int r,cs int &ql,cs int &qr,cs int &val){
if(ql<=l&&r<=qr)return pushnow(k,l,r,val);
pushdown(k,l,r);
int mid=(l+r)>>1;
if(ql<=mid)modify(k<<1,l,mid,ql,qr,val);
if(qr>mid)modify(k<<1|1,mid+1,r,ql,qr,val);
pushup(k);
}
int n,m;
inline void addpath(int u,int v,int val){
while(top[u]!=top[v]){
if(dep[top[v]]>dep[top[u]])swap(u,v);
modify(1,1,n,in[top[u]],in[u],val);
u=fa[top[u]];
}
if(dep[u]<dep[v])swap(u,v);
modify(1,1,n,in[v],in[u],val);
}
inline ll querypath(int u,int v){
ll res=0;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]])swap(u,v);
res+=query(1,1,n,in[top[u]],in[u]);
u=fa[top[u]];
}
if(dep[u]<dep[v])swap(u,v);
return res+query(1,1,n,in[v],in[u]);
}
inline int find(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]])swap(u,v);
if(fa[top[u]]==v)return top[u];
u=fa[top[u]];
}
if(dep[u]<dep[v])swap(u,v);
return son[v];
}
inline void addsubtree(int u,int val){
if(u==root)return modify(1,1,n,1,n,val);
int lca=LCA(u,root);
if(lca!=u)return modify(1,1,n,in[u],out[u],val);
int son=find(u,root);
modify(1,1,n,1,n,val);
modify(1,1,n,in[son],out[son],-val);
}
inline ll querysubtree(int u){
if(u==root)return query(1,1,n,1,n);
int lca=LCA(u,root);
if(lca!=u)return query(1,1,n,in[u],out[u]);
int son=find(u,root);
return query(1,1,n,1,n)-query(1,1,n,in[son],out[son]);
}
signed main(){
n=getint();
for(int re i=1;i<=n;++i)val[i]=getint();
for(int re i=2;i<=n;++i){
int fa=getint();
addedge(fa,i);
}
tree_dissection();
build(1,1,n);
m=getint();
while(m--){
int op=getint(),u=getint();
switch(op){
case 1:{root=u;break;}
case 2:{
int v=getint(),val=getint();
addpath(u,v,val);
break;
}
case 3:{
int val=getint();
addsubtree(u,val);
break;
}
case 4:{
int v=getint();
outint(querypath(u,v));pc('\n');
break;
}
case 5:{
outint(querysubtree(u));pc('\n');
break;
}
}
}
return 0;
}