題目鏈接: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。
分兩種情況考慮:
- \(u,v\) 不屬於同一個塊:將屬於編號較大的一個塊的節點跳至它的前驅。
- \(u,v\) 屬於同一個塊:接下來還是要分兩種情況:
- \(u\) 的前驅和 \(v\) 的前驅不同:將 \(u,v\) 同時跳至各自的前驅。
- \(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;
}