描述
给定一棵 n n n 个节点的树,初始时该树的根为 1 1 1 号节点,每个节点有一个给定的权值。下面依次进行 m m m 个操作,操作分为如下五种类型:
换根:将一个指定的节点设置为树的新根。
修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。
修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。
询问路径:询问某条路径上节点的权值和。
询问子树:询问某个子树内节点的权值和。
输入
第一行为一个整数 n n n,表示节点的个数。
第二行
n
n
n个整数表示第
i
i
i个节点的初始权值
a
i
a_i
ai
第三行
n
−
1
n−1
n−1个整数,表示
i
+
1
i+1
i+1 号节点的父节点编号
f
i
+
1
f_{i+1}
fi+1
(
1
<
f
i
+
1
<
n
)
(1<f_{i+1}<n)
(1<fi+1<n)
第四行一个整数
m
m
m,表示操作个数。
接下来
m
m
m 行,每行第一个整数表示操作类型编号:
(
1
<
u
,
v
<
n
)
(1<u,v<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 4 4 或 5 5 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<n,m,k,ai<10^5 1<n,m,k,ai<105数据有一定梯度。
题解:
其实主要难度就在于换根的操作,会对子树操作造成影响
我们考虑换根 r o o t root root对一个子树询问的影响
- 如果 r o o t = u : root=u: root=u:
那么子树操作就相当于是对整棵树操作了
- 如果 l c a ( u , r o o t ) ̸ = u lca(u,root)\not=u lca(u,root)̸=u
也就是说 u u u的子树并没有变,那仍然就是对 d f s dfs dfs上的一段区间了
- 如果 l c a ( u , r o o t ) = u lca(u,root)=u lca(u,root)=u
也就是说 r o o t root root在 u u u的子树里,那我们要修改的也就是 S u b t r e e u Subtree_u Subtreeu的补集
那只用找到 u u u到 r o o t root root的第一个儿子,对整棵树操作一次后再对 S u b t r e e s o n u Subtree_{son_u} Subtreesonu减去贡献就可以了
具体看代码理解
#include<bits/stdc++.h>
using namespace std;
#define gc getchar
#define ll long long
inline int read(){
char ch=gc();
int res=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-f;ch=gc();}
while(isdigit(ch)){res=(res<<3)+(res<<1)+(ch^48),ch=gc();}
return res*f;
}
#undef gc
const int N=100005;
int n,a[N],m;
namespace Seg{
#define lc (u<<1)
#define rc ((u<<1)|1)
#define mid ((l+r)>>1)
int idx[N];
ll tr[N<<2],add[N<<2];
inline void pushup(int u){
tr[u]=tr[lc]+tr[rc];
}
inline void pushdown(int u,int l,int r){
if(!add[u])return;
add[lc]+=add[u],add[rc]+=add[u],tr[lc]+=1ll*(mid-l+1)*add[u],tr[rc]+=1ll*(r-mid)*add[u],add[u]=0;
}
void buildtree(int u,int l,int r){
if(l==r){
tr[u]=a[idx[l]];return;
}
buildtree(lc,l,mid);
buildtree(rc,mid+1,r);
pushup(u);
}
void update(int u,int l,int r,int st,int des,ll k){
if(st<=l&&r<=des){
add[u]+=k,tr[u]+=1ll*(r-l+1)*k;return;
}
pushdown(u,l,r);
if(st<=mid)update(lc,l,mid,st,des,k);
if(mid<des)update(rc,mid+1,r,st,des,k);
pushup(u);
}
ll query(int u,int l,int r,int st,int des){
if(st<=l&&r<=des){
return tr[u];
}
pushdown(u,l,r);ll res=0;
if(st<=mid)res+=query(lc,l,mid,st,des);
if(mid<des)res+=query(rc,mid+1,r,st,des);
pushup(u);return res;
}
}
using namespace Seg;
namespace SLPF{
int adj[N],nxt[N<<1],to[N<<1],cnt,rt;
int son[N],siz[N],dep[N],fa[N],pos[N],out[N],top[N],tot;
inline void addedge(int u,int v){
nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v;
}
void dfs1(int u){
siz[u]=1;
for(int e=adj[u];e;e=nxt[e]){
int v=to[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;
}
}
void dfs2(int u,int tp){
pos[u]=++tot,idx[tot]=u;top[u]=tp;
if(!son[u])return void (out[u]=tot);
dfs2(son[u],tp);
for(int e=adj[u];e;e=nxt[e]){
int v=to[e];
if(v==fa[u]||v==son[u])continue;
dfs2(v,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);
u=fa[top[u]];
}
return dep[u]>dep[v]?v: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 pathupdate(int u,int v,ll k){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]])swap(u,v);
update(1,1,n,pos[top[u]],pos[u],k);
u=fa[top[u]];
}
if(dep[u]<dep[v])swap(u,v);
update(1,1,n,pos[v],pos[u],k);
}
inline void subupdate(int u,ll k){
if(u==rt)return update(1,1,n,1,n,k);
int lca=Lca(u,rt);
if(lca!=u)return update(1,1,n,pos[u],out[u],k);
int son=find(u,rt);
update(1,1,n,1,n,k);
update(1,1,n,pos[son],out[son],-k);
}
inline ll pathquery(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,pos[top[u]],pos[u]);
u=fa[top[u]];
}
if(dep[u]<dep[v])swap(u,v);
res+=query(1,1,n,pos[v],pos[u]);
return res;
}
inline ll subquery(int u){
if(u==rt)return query(1,1,n,1,n);
int lca=Lca(u,rt);
if(lca!=u)return query(1,1,n,pos[u],out[u]);
int son=find(u,rt);ll res=0;
res+=query(1,1,n,1,n);
res-=query(1,1,n,pos[son],out[son]);
return res;
}
}
using namespace SLPF;
int main(){
n=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=2;i<=n;i++){
int v=read();
addedge(i,v),addedge(v,i);
}
m=read();
dfs1(rt=1),dfs2(1,1);
buildtree(1,1,n);
for(int i=1;i<=m;i++){
int op=read();
if(op==1)rt=read();
else if(op==2){
int u=read(),v=read(),k=read();
pathupdate(u,v,k);
}
else if(op==3){
int u=read(),k=read();
subupdate(u,k);
}
else if(op==4){
int u=read(),v=read();
cout<<pathquery(u,v)<<'\n';
}
else{
int u=read();
cout<<subquery(u)<<'\n';
}
}
}