Codeforces 1491H. Yuezheng Ling and Dynamic Tree 題解


題目鏈接:H. Yuezheng Ling and Dynamic Tree

題目大意:給定一棵大小為 \(n\) 的一 \(1\) 為根節點的樹,樹用如下方式給出:輸入 \(a_2,a_3,\dots,a_n\),保證 \(1\leq a_i<i\),將 \(a_i\)\(i\) 連邊形成一棵樹。

接下來有兩種操作:

  • 1 l r x\(a_i=\max(a_i-x,1)(l\leq i\leq r)\)
  • 2 u v 查詢在當前的 \(a\) 數組構成的樹上 \(u,v\) 的 LCA。

兩種操作總數一共有 \(q\) 個。

\(2\leq n,q\leq 10^5\)\(2\leq l\leq r\leq n\)\(1\leq x\leq 10^5\)\(1\leq u,v\leq 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(n\sqrt{n}+q\sqrt{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\n",query(u,v));
		}
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM