1021 - 【模板】树链剖分+换根

版权声明:虽然我只是个小蒟蒻但转载也请注明出处哦 https://blog.csdn.net/weixin_42557561/article/details/83240676

树链剖分

描述

给定一棵 n 个节点的树,初始时该树的根为 1 号节点,每个节点有一个给定的权值。下面依次进行 m 个操作,操作分为如下五种类型:

换根:将一个指定的节点设置为树的新根。

修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。

修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。

询问路径:询问某条路径上节点的权值和。

询问子树:询问某个子树内节点的权值和。

输入

第一行为一个整数 n,表示节点的个数。
第二行 n 个整数表示第i个节点的初始权值 ai
第三行 n−1 个整数,表示i+1 号节点的父节点编号 fi+1 (1⩽fi+1⩽n)
第四行一个整数 m,表示操作个数。
接下来 m 行,每行第一个整数表示操作类型编号:(1⩽u,v⩽n)
若类型为 1,则接下来一个整数 u,表示新根的编号。
若类型为 2,则接下来三个整数 u,v,k,分别表示路径两端的节点编号以及增加的权值。
若类型为3,则接下来两个整数 u,k,分别表示子树根节点编号以及增加的权值。
若类型为 4,则接下来两个整数u,v,表示路径两端的节点编号。
若类型为 5,则接下来一个整数 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% 的数据,1⩽n,m,k,ai⩽105。数据有一定梯度。

分析

是个很好的板子题,集结了树链剖分的大部分操作,而且还有个新操作换根,妙啊

重点讲一下如何换根。
我们很容易发现换根的操作对路径修改&查询是没有影响的
只对子树的操作有影响

考虑root为当前的根,u为所求子树的根,lca为原图中的lca
1.u==root,那么u 的子树就是整棵树。
2.LCA(root,u)≠u,即root不在u的子树中。那么u现在的子树就是原来的子树
3.LCA(root,u)=u,即u 在原来的树中是root 的祖先。那么我们找到u 到root 路径上的第一个儿子。这个儿子对应的原树中的子树,就是现在u 的子树的补集。

我们在代码中再解释一番

代码

#include<bits/stdc++.h>
#define N 100005
#define lc (k<<1)
#define rc (k<<1)|1
#define in read()
#define int long long
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
int n,m,a[N],root;
int top[N],idx[N<<2],pos[N<<2],tot=0;
int fa[N],son[N],sze[N],dep[N];
int lzy[N<<2],sum[N<<2];
int nxt[N<<1],to[N<<1],head[N],ecnt=0;
inline void add(int x,int y){
	nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;
	nxt[++ecnt]=head[y];head[y]=ecnt;to[ecnt]=x;
}
void dfs1(int u,int fu){
	sze[u]=1;
	for(int e=head[u];e;e=nxt[e]){
		int v=to[e];
		if(v==fu) continue;
		dep[v]=dep[u]+1;fa[v]=u;
		dfs1(v,u);
		sze[u]+=sze[v];
		if(sze[v]>sze[son[u]]) son[u]=v;
	}
}
void dfs2(int u){
	if(son[u]){
		int v=son[u];
		idx[pos[v]=++tot]=v;
		top[v]=top[u];
		dfs2(v);
	}
	for(int e=head[u];e;e=nxt[e]){
		int v=to[e];
		if(v==fa[u]||v==son[u]) continue;//
		idx[pos[v]=++tot]=v;
		top[v]=v;
		dfs2(v);
	}
}
inline void build(int k,int l,int r){
	if(l==r){
		sum[k]=a[idx[l]];
		return ;
	}
	int mid=l+r>>1;
	build(lc,l,mid);build(rc,mid+1,r);
	sum[k]=sum[lc]+sum[rc];
}
inline void pushdown(int k,int l,int r){
	int mid=l+r>>1;
	lzy[lc]+=lzy[k];sum[lc]+=(mid-l+1)*lzy[k];
	lzy[rc]+=lzy[k];sum[rc]+=(r-mid)*lzy[k];
	lzy[k]=0;
}
inline void modify(int k,int l,int r,int x,int y,int v){
	if(x<=l&&r<=y){
		lzy[k]+=v;sum[k]+=(r-l+1)*v;
		return;
	}
	if(lzy[k]) pushdown(k,l,r);
	int mid=l+r>>1;
	if(x<=mid) modify(lc,l,mid,x,y,v);
	if(y>mid) modify(rc,mid+1,r,x,y,v);
	sum[k]=sum[lc]+sum[rc];
}
inline void modifypath(int x,int y,int v){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		modify(1,1,n,pos[top[x]],pos[x],v);
		x=fa[top[x]];
	}
	if(dep[x]<dep[y]) swap(x,y);
	modify(1,1,n,pos[y],pos[x],v);
}
inline int getlca(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]>dep[y]?y:x;
}
inline int find(int x,int y){//找 x 到 y 路径上第一个儿子 
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		if(fa[top[x]]==y) return top[x];
		x=fa[top[x]];
	}
	if(dep[x]<dep[y]) swap(x,y);
	return son[y];
}
inline void modifysub(int u,int k){
	if(u==root)  return modify(1,1,n,1,n,k);
	int lca=getlca(u,root);
	if(lca!=u) return modify(1,1,n,pos[u],pos[u]+sze[u]-1,k);
	else {
		int child=find(u,root);
		modify(1,1,n,1,n,k);
		modify(1,1,n,pos[child],pos[child]+sze[child]-1,-k);
		return;
	}
}

inline int query(int k,int l,int r,int x,int y){
	if(x<=l&&r<=y){return sum[k];}
	int res=0;
	if(lzy[k]) pushdown(k,l,r);
	int mid=l+r>>1;
	if(x<=mid) res+=query(lc,l,mid,x,y);
	if(y>mid) res+=query(rc,mid+1,r,x,y);
	return res;
} 
inline int querypath(int x,int y){
	int res=0;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		res+=query(1,1,n,pos[top[x]],pos[x]);
		x=fa[top[x]];
	}
	if(dep[x]<dep[y]) swap(x,y);
	res+=query(1,1,n,pos[y],pos[x]);
	return res;
}
inline int querysub(int x){
	if(x==root) return query(1,1,n,1,n);
	int lca=getlca(x,root);
	if(lca!=x) return query(1,1,n,pos[x],pos[x]+sze[x]-1);
	int child=find(x,root);
	int res=query(1,1,n,1,n);
	res-=query(1,1,n,pos[child],pos[child]+sze[child]-1);
	return res;
}
signed main(){
	n=in;root=1;
	int i,j,k;
	for(i=1;i<=n;++i) a[i]=in;
	for(i=2;i<=n;++i){ j=in;add(i,j); }
	dfs1(1,0);
	idx[1]=1;pos[1]=++tot;top[1]=1;
	dfs2(1);
	build(1,1,n);
	m=in;int u,v;
	while(m--){
		int op=in;
		if(op==1){ u=in;root=u; }
		else if(op==2){
			u=in;v=in;k=in;
			modifypath(u,v,k);
		}
		else if(op==3){
			u=in;k=in;
			modifysub(u,k);
		}
		else if(op==4){
			u=in;v=in;
			cout<<querypath(u,v)<<'\n';
		}
		else {
			u=in;
			cout<<querysub(u)<<'\n';
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42557561/article/details/83240676