【模板】树链剖分+换根

描述

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

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

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

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

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

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

输入

第一行为一个整数 nn,表示节点的个数。

第二行nn个整数表示第ii个节点的初始权值 aia_i
第三行 n1n−1个整数,表示i+1i+1 号节点的父节点编号 fi+1f_{i+1} (1<fi+1<n)(1<f_{i+1}<n)
第四行一个整数 mm,表示操作个数。

接下来 mm 行,每行第一个整数表示操作类型编号:(1<u,v<n)(1<u,v<n)
若类型为 11,则接下来一个整数 uu,表示新根的编号。

若类型为 22,则接下来三个整数 u,v,ku,v,k,分别表示路径两端的节点编号以及增加的权值。

若类型为33,则接下来两个整数 u,ku,k,分别表示子树根节点编号以及增加的权值。

若类型为 44,则接下来两个整数 u,vu,v,表示路径两端的节点编号。

若类型为 55,则接下来一个整数 uu,表示子树根节点编号。

输出

对于每一个类型为 4455 的操作,输出一行一个整数表示答案。

样例输入

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\%的数据,1<n,m,k,ai<1051<n,m,k,ai<10^5数据有一定梯度。


题解:

其实主要难度就在于换根的操作,会对子树操作造成影响

我们考虑换根rootroot对一个子树询问的影响

  1. 如果root=u:root=u:

那么子树操作就相当于是对整棵树操作了

  1. 如果lca(u,root)̸=ulca(u,root)\not=u

也就是说uu的子树并没有变,那仍然就是对dfsdfs上的一段区间了

  1. 如果lca(u,root)=ulca(u,root)=u

也就是说rootrootuu的子树里,那我们要修改的也就是SubtreeuSubtree_u的补集

那只用找到uurootroot的第一个儿子,对整棵树操作一次后再对SubtreesonuSubtree_{son_u}减去贡献就可以了

具体看代码理解


#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';
		}
	}
}
posted @ 2019-01-22 10:04  Stargazer_cykoi  阅读(228)  评论(0编辑  收藏  举报