#WD231105B. 异或树(xortree)--自编题题解


网址

题目详情 - 【NOIP模板】树链剖分 - LOJ模板 - Super

题解 - 【NOIP模板】树链剖分 - LOJ模板 - Super

数据

题解

给你一棵有点权的树,开始时根为 1 号点,请你实现以下操作:

  • 换根
  • 一条链上点权加
  • 一个子树内点权加
  • 询问一条链上点权的和
  • 询问一个子树内的点权和

操作大多数和模板题一样,且不用取模,但是多了一个换根操作。

所以对于链上的操作是不会影响的,只有子树部分操作有影响。

首先考虑暴力,每次换根后重构,那么复杂度显然接受不了,所以我们要考虑不重构,所以开始的时候就以一号点为根,先把树剖了,线段树建出来。

此时,我们就要考虑不同的根和子树操作该如何实现,我们假设根为 r,操作子树的根节点为 u,那么有如下几种情况(原来的子树是指的在根为 1 时的子树):

当 u=r,此时操作范围就是整个树,将整个树加或者求和即可。

png => png

当 r 不在 u 的原来的子树里面时,操作范围就是原来的子树。

png => png

当 r 在 u 的子树内时,情况就比较复杂,操作范围就为整个树减去 u 到 r 路径上深度最小的点(不包含 u 点)的原来的子树。

png => png

比如上面这个例子就是,我们现在 6 号点为根,查询 2 号点,它的子树就是 1,3,5,也就是原来的整个树减去 4 号点的原来的子树剩余部分,而 4 号点刚好是 6 ∼ 2 上不包含 2 的深度最小的点。

所以我们再子树操作的时候再稍微判断一下,分类讨论求一下就好啦,求路上深度最小的点可以倍增往上跳,也可以直接树剖的线段树维护即可。

#include<bits/stdc++.h>
#define For(i,l,r) for(int i=l;i<=r;i++)
#define Rof(i,l,r) for(int i=l;i>=r;i--)
using namespace std;
#define P pair<int,int> 
#define int long long
inline int rd(){int x;return cin>>x,x;}
const int N = 2012345;
bool ST;

int n,m,a[N],root=1;
vector<int> ft[N];

int son[N],siz[N],dep[N],fa[N];
void dfs_son(int x,int father){
	siz[x]=1,dep[x]=dep[father]+1,fa[x]=father;
	for(auto y:ft[x]){
		if(y==father) continue;
		dfs_son(y,x);
		siz[x]+=siz[y];
		son[x]=siz[son[x]]>=siz[y]?son[x]:y;
	}
}

int top[N],w[N],id[N],cnt;
void dfs_top(int x,int tp){
	top[x]=tp,id[x]=++cnt,w[cnt]=a[x];
	if(!son[x]) return;
	dfs_top(son[x],tp);
	for(auto y:ft[x]){
		if(y==fa[x]||y==son[x]) continue;
		dfs_top(y,y);
	}
}

#define mid ((l+r)>>1)
#define ls (p<<1)
#define rs (p<<1|1)
struct Tr{int sum,add,siz;}tr[N<<2|3];

void pushup(Tr &T,Tr L,Tr R){T.sum=L.sum+R.sum;}

void build(int p,int l,int r){
	tr[p]={w[l],0,r-l+1};
	if(l==r) return;
	build(ls,l,mid),build(rs,mid+1,r);
	pushup(tr[p],tr[ls],tr[rs]); 
}

void pushdown(int p){
	if(tr[p].add){
		tr[ls].sum+=tr[ls].siz*tr[p].add;
		tr[rs].sum+=tr[rs].siz*tr[p].add;
		tr[ls].add+=tr[p].add;
		tr[rs].add+=tr[p].add;
		tr[p].add=0;
	}
}

void update(int p,int l,int r,int L,int R,int k){
	if(L<=l&&r<=R){
		tr[p].sum+=tr[p].siz*k;
		tr[p].add+=k;return;
	}
	pushdown(p);
	if(L<=mid) update(ls,l,mid,L,R,k);
	if(R>mid) update(rs,mid+1,r,L,R,k);
	pushup(tr[p],tr[ls],tr[rs]);
}

inline Tr ask(int p,int l,int r,int L,int R){
	if(L<=l&&r<=R) return tr[p];
	pushdown(p);
	if(R<=mid) return ask(ls,l,mid,L,R);
	if(L>mid) return ask(rs,mid+1,r,L,R);
	Tr T;return pushup(T,ask(ls,l,mid,L,mid),ask(rs,mid+1,r,mid+1,R)),T;
}

void update_path(int x,int y,int k){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		update(1,1,n,id[top[x]],id[x],k);
		x=fa[top[x]];
	}
	if(dep[x]<dep[y]) swap(x,y);
	update(1,1,n,id[y],id[x],k);
}

void update_tree(int x,int k){
	if(id[x]==id[root]) update(1,1,n,id[1],id[1]+siz[1]-1,k);else//x为根节点 
	if(id[x]<id[root]&&id[root]<=id[x]+siz[x]-1){//root在x子树中 
		update(1,1,n,id[1],id[1]+siz[1]-1,k);//先加上总数
		int y=root;//从root开始跳 
		while(fa[top[y]]!=x && top[y]!=top[x])y=fa[top[y]];
		if(fa[top[y]]==x) y=top[y];//root在x的轻链上 
		if(top[y]==top[x]) y=son[x];//root在x的重链上 
		update(1,1,n,id[y],id[y]+siz[y]-1,-k);
	}
	else{update(1,1,n,id[x],id[x]+siz[x]-1,k);}//root不在x子树中
}

int ask_path(int x,int y){
	int res=0;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		res+=ask(1,1,n,id[top[x]],id[x]).sum;
		x=fa[top[x]];
	}
	if(dep[x]<dep[y]) swap(x,y);
	return res+ask(1,1,n,id[y],id[x]).sum;
}

int ask_tree(int x){
	if(id[x]==id[root]) return ask(1,1,n,id[1],id[1]+siz[1]-1).sum;else
	if(id[x]<=id[root]&&id[root]<=id[x]+siz[x]-1){
		int res=ask(1,1,n,id[1],id[1]+siz[1]-1).sum; 
		int y=root;
		while(fa[top[y]]!=x && top[y]!=top[x])y=fa[top[y]];
		if(fa[top[y]]==x) y=top[y];
		if(top[y]==top[x]) y=son[x];
		return res-ask(1,1,n,id[y],id[y]+siz[y]-1).sum;
	}else{return ask(1,1,n,id[x],id[x]+siz[x]-1).sum;}
}

bool ED;
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cerr<<abs(&ST-&ED)/1024./1024.<<"MB\n";
	cin>>n;For(i,1,n) cin>>a[i];
	For(i,1,n-1){
		int x=i+1,y;cin>>y;
		ft[x].push_back(y);
		ft[y].push_back(x);
	}
	dfs_son(root,0);
	dfs_top(root,root);
	build(1,1,n);
	cin>>m;
	while(m--){
		int k = rd(),x,y,z;
		switch(k){
			case 1:{cin>>root;break;}
			case 2:{cin>>x>>y>>z;update_path(x,y,z);break;}
			case 3:{cin>>x>>y;update_tree(x,y);break;}
			case 4:{cin>>x>>y;cout<<ask_path(x,y)<<'\n';break;}
			case 5:{cin>>x;cout<<ask_tree(x)<<'\n';break;}
		}
	}
	return 0;
}

文章作者: WolfDeer
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 WolfDeer !
  目录