Codeforces 1491H. Yuezheng Ling and Dynamic Tree 题解

题目链接:H. Yuezheng Ling and Dynamic Tree

题目大意:给定一棵大小为 (n) 的一 (1) 为根节点的树,树用如下方式给出:输入 (a_2,a_3,dots,a_n),保证 (1leq a_i<i),将 (a_i)(i) 连边形成一棵树。

接下来有两种操作:

  • 1 l r x(a_i=max(a_i-x,1)(lleq ileq r))
  • 2 u v 查询在当前的 (a) 数组构成的树上 (u,v) 的 LCA。

两种操作总数一共有 (q) 个。

(2leq n,qleq 10^5)(2leq lleq rleq n)(1leq xleq 10^5)(1leq u,vleq n)


题解:将 (a) 序列分块,假设块长为 (B),对于每一个节点,预处理出它的祖先中编号最大的和它不再同一个块的祖先,为了方便,在下文中我们称其为当前点的前驱,我们先考虑如何求两个点的 LCA。

分两种情况考虑:

  1. (u,v) 不属于同一个块:将属于编号较大的一个块的节点跳至它的前驱。
  2. (u,v) 属于同一个块:接下来还是要分两种情况:
    1. (u) 的前驱和 (v) 的前驱不同:将 (u,v) 同时跳至各自的前驱。
    2. (u) 的前驱和 (v) 的前驱相同:此时可以轮流跳 (u,v) 中编号较大的点的父亲直至两个点相等。

接下来分析查询的时间复杂度:因为前驱最多不超过 (frac{n}{B}) 个,而暴力跳父亲不超过 (B) 次,所以时间复杂度是 (O(B+frac{n}{B}))

然后我们再来考虑修改:按照序列分块的惯例,我们分整块和散块来考虑,对于散块,直接暴力修改然后暴力重构前驱即可。

但是对于整块,我们需要打懒惰标记,但是这种时候无法维护前驱的修改,因为修改一个块的前驱是需要遍历整块的。

但是我们发现,一个块最多在经过 (B) 次修改之后,块中每一个数的父亲都会在这个块之前,也就是说,我们对于一个块需要暴力重构前驱的次数最多是 (B) 次,之后的可以直接通过懒标记直接减来解决。

那么分析修改的复杂度,对于散块修改是 (O(B)) 的,对于整块修改(不考虑重构前驱)是 (O(frac{n}{B})) 的,对于重构前驱的总时间复杂度是 (O(nB)) 的。

综上,若取 (B=sqrt{n}),则可以获得 (O(nsqrt{n}+qsqrt{n})) 的时间复杂度,可以通过本题。

代码:

#include <cstdio>
#include <algorithm>
const int Maxb=300;
const int Maxn=100000;
const int Maxv=(Maxn-1)/Maxb+1;
int n,q;
int num[Maxv+5],lazy[Maxv+5];
int fa[Maxn+5],out[Maxn+5];
int find_belong(int x){
	return (x-1)/Maxb+1;
}
int find_bel_l(int x){
	return (x-1)*Maxb+1;
}
int find_bel_r(int x){
	return std::min(n,x*Maxb);
}
void build(int x){
	for(int i=find_bel_l(x);i<=find_bel_r(x);i++){
		fa[i]=std::max(1,fa[i]-lazy[x]);
	}
	lazy[x]=0;
	for(int i=find_bel_l(x);i<=find_bel_r(x);i++){
		if(fa[i]<find_bel_l(x)){
			out[i]=fa[i];
		}
		else{
			out[i]=out[fa[i]];
		}
	}
}
int find_out(int x){
	return std::max(1,out[x]-lazy[find_belong(x)]);
}
int find_fa(int x){
	return std::max(1,fa[x]-lazy[find_belong(x)]);
}
void update(int l,int r,int x){
	int bel_l=find_belong(l),bel_r=find_belong(r);
	if(bel_l==bel_r){
		for(int i=l;i<=r;i++){
			fa[i]=std::max(1,fa[i]-x);
		}
		build(bel_l);
		return;
	}
	for(int i=l;i<=find_bel_r(bel_l);i++){
		fa[i]=std::max(1,fa[i]-x);
	}
	build(bel_l);
	for(int i=find_bel_l(bel_r);i<=r;i++){
		fa[i]=std::max(1,fa[i]-x);
	}
	build(bel_r);
	for(int i=bel_l+1;i<bel_r;i++){
		num[i]++;
		lazy[i]=std::min(n,lazy[i]+x);
		if(num[i]<=Maxb){
			build(i);
		}
	}
}
int query(int u,int v){
	while(u!=v){
		if(find_belong(u)<find_belong(v)){
			std::swap(u,v);
		}
		if(find_belong(u)>find_belong(v)){
			u=find_out(u);
		}
		else{
			if(find_out(u)!=find_out(v)){
				u=find_out(u);
				v=find_out(v);
			}
			else{
				while(u!=v){
					if(u<v){
						std::swap(u,v);
					}
					u=find_fa(u);
				}
			}
		}
	}
	return u;
}
int main(){
	scanf("%d%d",&n,&q);
	for(int i=2;i<=n;i++){
		scanf("%d",&fa[i]);
	}
	for(int i=1;i<=find_belong(n);i++){
		build(i);
	}
	for(int i=1;i<=q;i++){
		int op;
		scanf("%d",&op);
		if(op==1){
			int l,r,x;
			scanf("%d%d%d",&l,&r,&x);
			update(l,r,x);
		}
		else{
			int u,v;
			scanf("%d%d",&u,&v);
			printf("%d
",query(u,v));
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/withhope/p/14462136.html