【模板】树链剖分+换根
描述
给定一棵 个节点的树,初始时该树的根为 号节点,每个节点有一个给定的权值。下面依次进行 个操作,操作分为如下五种类型:
换根:将一个指定的节点设置为树的新根。
修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。
修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。
询问路径:询问某条路径上节点的权值和。
询问子树:询问某个子树内节点的权值和。
输入
第一行为一个整数 ,表示节点的个数。
第二行个整数表示第个节点的初始权值
第三行 个整数,表示 号节点的父节点编号
第四行一个整数 ,表示操作个数。
接下来 行,每行第一个整数表示操作类型编号:
若类型为 ,则接下来一个整数 ,表示新根的编号。
若类型为 ,则接下来三个整数 ,分别表示路径两端的节点编号以及增加的权值。
若类型为,则接下来两个整数 ,分别表示子树根节点编号以及增加的权值。
若类型为 ,则接下来两个整数 ,表示路径两端的节点编号。
若类型为 ,则接下来一个整数 ,表示子树根节点编号。
输出
对于每一个类型为 或 的操作,输出一行一个整数表示答案。
样例输入
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
提示
对于 的数据,数据有一定梯度。
题解:
其实主要难度就在于换根的操作,会对子树操作造成影响
我们考虑换根对一个子树询问的影响
- 如果
那么子树操作就相当于是对整棵树操作了
- 如果
也就是说的子树并没有变,那仍然就是对上的一段区间了
- 如果
也就是说在的子树里,那我们要修改的也就是的补集
那只用找到到的第一个儿子,对整棵树操作一次后再对减去贡献就可以了
具体看代码理解
#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';
}
}
}