【题目地址】
其实这个题不是个单纯的模板,所以要做单纯无脑的模板或者学习树链剖分的可以去这里【Luogu-树链剖分】,这里不会讲树链剖分的原理与实现。
开始想写一些模板题目,luogu的写得差不多了,所以就去loj看看,结果就看到了这道,emmmm
题目大意
给你一棵有点权的树,开始时根为1号点,请你实现以下操作:
- 换根
- 一条链上点权加
- 一个子树内点权加
- 询问一条链上点权的和
- 询问一个子树内的点权和
操作大多数和luogu的模板题一样,且不用取模,但是多了一个换根操作。
所以对于链上的操作是不会影响的,只有子树部分操作有影响。
首先考虑暴力,每次换根后重构,那么复杂度显然接受不了,所以我们要考虑不重构,所以开始的时候就以一号点为根,先把树剖了,线段树建出来。
此时,我们就要考虑不同的根和子树操作该如何实现,我们假设根为 r r r,操作子树的根节点为 u u u,那么有如下几种情况(原来的子树是指的在根为1时的子树):
- 当 u = r u=r u=r,此时操作范围就是整个树,将整个树加或者求和即可。
=
=
=
=
=
=
=
=
=
>
=========>
=========>
- 当 r r r不在 u u u的原来的子树里面时,操作范围就是原来的子树。
=
=
=
=
=
=
=
=
=
>
=========>
=========>
- 当 r r r在 u u u的子树内时,情况就比较复杂,操作范围就为整个树减去 u u u到 r r r路径上深度最小的点(不包含 u u u点)的原来的子树。
=
=
=
=
=
=
=
=
=
>
=========>
=========>
比如上面这个例子就是,我们现在6号点为根,查询2号点,它的子树就是1,3,5,也就是原来的整个树减去4号点的原来的子树剩余部分,而4号点刚好是 6 ∼ 2 6\sim 2 6∼2上不包含2的深度最小的点。
所以我们再子树操作的时候再稍微判断一下,分类讨论求一下就好啦,求路上深度最小的点可以倍增往上跳,也可以直接树剖的线段树维护即可。
长长的代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int M=1e5+10;
const int inf=1e9;
int n,m,lg;
ll val[M];
struct ss{
int to,last;
ss(){}
ss(int a,int b):to(a),last(b){}
}g[M<<1];
int head[M],cnt;
void add(int a,int b){
g[++cnt]=ss(b,head[a]);head[a]=cnt;
g[++cnt]=ss(a,head[b]);head[b]=cnt;
}
int dep[M],sze[M],top[M],son[M],f[M];
int num[M],rf[M],tim,root=1;
void dfs1(int a){
sze[a]=1;
for(int i=head[a];i;i=g[i].last){
if(g[i].to==f[a]) continue;
dep[g[i].to]=dep[a]+1;
f[g[i].to]=a;
dfs1(g[i].to);
sze[a]+=sze[g[i].to];
if(!son[a]||sze[son[a]]<sze[g[i].to])
son[a]=g[i].to;
}
}
void dfs2(int a,int b){
top[a]=b;rf[num[a]=++tim]=a;
if(!son[a]) return;
dfs2(son[a],b);
for(int i=head[a];i;i=g[i].last){
if(g[i].to==f[a]||g[i].to==son[a]) continue;
dfs2(g[i].to,g[i].to);
}
}
ll sum[M<<2],lazy[M<<2];
int minp[M<<2];
int tmin(int a,int b){
if(a==inf)return b;
if(b==inf)return a;
if(dep[a]<dep[b])return a;
else return b;
}
void pushup(int o){
sum[o]=sum[o<<1]+sum[o<<1|1];
minp[o]=tmin(minp[o<<1],minp[o<<1|1]);
}
void pushdown(int o,int l,int r,int mid){
if(!lazy[o]) return;
lazy[o<<1]+=lazy[o];
lazy[o<<1|1]+=lazy[o];
sum[o<<1]+=(mid-l+1)*lazy[o];
sum[o<<1|1]+=(r-mid)*lazy[o];
lazy[o]=0;
}
void build(int o,int l,int r){
if(l==r){
sum[o]=val[rf[l]];
minp[o]=rf[l];
return;
}
int mid=l+r>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushup(o);
}
void update(int o,int l,int r,int L,int R,ll v){
if(L<=l&&r<=R){
sum[o]+=(r-l+1)*v;
lazy[o]+=v;
return;
}
int mid=l+r>>1;
pushdown(o,l,r,mid);
if(L<=mid) update(o<<1,l,mid,L,R,v);
if(R>mid) update(o<<1|1,mid+1,r,L,R,v);
pushup(o);
}
ll query(int o,int l,int r,int L,int R){
if(L<=l&&r<=R) return sum[o];
int mid=l+r>>1;
pushdown(o,l,r,mid);
if(R<=mid) return query(o<<1,l,mid,L,R);
else if(L>mid) return query(o<<1|1,mid+1,r,L,R);
else return query(o<<1,l,mid,L,R)+query(o<<1|1,mid+1,r,L,R);
}
int find(int o,int l,int r,int L,int R){
if(R<L)return inf;
if(L<=l&&r<=R) return minp[o];
int mid=l+r>>1;
if(R<=mid) return find(o<<1,l,mid,L,R);
else if(L>mid) return find(o<<1|1,mid+1,r,L,R);
else return tmin(find(o<<1,l,mid,L,R),find(o<<1|1,mid+1,r,L,R));
}
int lca(int a,int b){
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
a=f[top[a]];
}
if(dep[a]>dep[b])swap(a,b);
return a;
}
int Upto(int a,int b){
int ans=inf,tw=a;
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
if(top[a]!=b)ans=tmin(ans,find(1,1,n,num[top[a]],num[a]));
else ans=tmin(ans,find(1,1,n,num[top[a]]+1,num[a]));
a=f[top[a]];
}
if(dep[a]>dep[b])swap(a,b);
ans=tmin(ans,find(1,1,n,num[a]+1,num[b]));
return ans;
}
int check(int a){
if(a==root) return 0;
int L=lca(a,root);
if(L==a) return Upto(a,root);
else return -1;
}
void Add_Chain(int a,int b,ll w){
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
update(1,1,n,num[top[a]],num[a],w);
a=f[top[a]];
}
if(dep[a]>dep[b])swap(a,b);
update(1,1,n,num[a],num[b],w);
}
ll Ask_Chain(int a,int b){
ll ans=0;
while(top[a]!=top[b]){
if(dep[top[a]]<dep[top[b]])swap(a,b);
ans+=query(1,1,n,num[top[a]],num[a]);
a=f[top[a]];
}
if(dep[a]>dep[b])swap(a,b);
ans+=query(1,1,n,num[a],num[b]);
return ans;
}
void Add_Tree(int u,ll v){
int type=check(u);
if(!type){
update(1,1,n,1,n,v);
}else if(type>0){
update(1,1,n,1,n,v);
update(1,1,n,num[type],num[type]+sze[type]-1,-v);
}else{
update(1,1,n,num[u],num[u]+sze[u]-1,v);
}
}
ll Ask_Tree(int u){
int type=check(u);
if(!type){
return query(1,1,n,1,n);
}else if(type>0){
ll ans=query(1,1,n,num[type],num[type]+sze[type]-1);
return query(1,1,n,1,n)-ans;
}else{
return query(1,1,n,num[u],num[u]+sze[u]-1);
}
}
int opt,a,b,c;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",&val[i]);
for(int i=2;i<=n;i++){
scanf("%d",&a);
add(i,a);
}
dfs1(1);
dfs2(1,1);
build(1,1,n);
for(scanf("%d",&m);m--;){
scanf("%d",&opt);
if(opt==1){
scanf("%d",&a);
root=a;
}else if(opt==2){
scanf("%d%d%d",&a,&b,&c);
Add_Chain(a,b,c);
}else if(opt==3){
scanf("%d%d",&a,&b);
Add_Tree(a,b);
}else if(opt==4){
scanf("%d%d",&a,&b);
printf("%lld\n",Ask_Chain(a,b));
}else{
scanf("%d",&a);
printf("%lld\n",Ask_Tree(a));
}
}
return 0;
}