zoukankan      html  css  js  c++  java
  • 【模板】树链剖分+换根

    描述

    给定一棵 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) ot=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)<<'
    ';
    		}
    		else{
    			int u=read();
    			cout<<subquery(u)<<'
    ';
    		}
    	}
    }
    
  • 相关阅读:
    LuoGu P1006 传纸条
    LuoGu P1083 借教室
    动态规划-区间dp-Palindrome Removal
    咕果
    直径问题 Diameter Problems
    Contest 161
    ALBert
    Focal Loss
    Contest 159
    Contest 160
  • 原文地址:https://www.cnblogs.com/stargazer-cyk/p/10366345.html
Copyright © 2011-2022 走看看