《扶蘇的問題》題解

adsd45666發表於2024-06-02

《扶蘇的問題》題解

原題傳送門:P1253 扶蘇的問題

PS:請先閱讀完題面,在繼續觀看

題意概述:

​ 對於給定的數列 \({a_1,a_2,a_3……a_n}\) ,進行以下三個操作:

​ 1.change 將區間 \([L,R]\) 的值修改為 \(X\)

​ 2.add 將區間 \([L,R]\) 的值加上為 \(D\)

​ 3.query 查詢區間 \([L,R]\) 的最大值;

分析:

對於本題,為追求最佳時間複雜度,我們將使用兩個懶標記,分別記錄 \(change\)\(add\) 標記。

對於改與加標記這兩個標記,我們可以發現改標記的優先度要大於加標記(都覆蓋了,你還加啥呀),於是對於 \(push -down\) 操作來說,優先下放改標記。同時,在進行改標記的新增時,對於將要被打上改標記的線段,其加標記將失去作用,即歸零;

AC程式碼詳解

#include<bits/stdc++.h>
#define ll long long 
#define seq(q,w,e) for(int q=w;q<=e;q++)
using namespace std;
const ll maxn=1e6+10;
const ll maxx=4e6+10;
const ll inf=2e15;                                 //首先,inf太小會出錯
ll a[maxn],tree[maxx],tag1[maxx],tag2[maxx];       //tag1為加標記,tag2為改標記
ll ls(ll p){return p<<1;}
ll rs(ll p){return p<<1|1;}
void push_up(ll p){
	tree[p]=max(tree[ls(p)],tree[rs(p)]);          //求最大值的線段樹維護
}
void build_tree(ll p,ll pl,ll pr){
	tag1[p]=0;tag2[p]=-inf;                        //其次,tag1,tag2不賦初值也會出錯
	if(pl==pr){
		tree[p]=a[pl];
		return;
	}
	ll mid=(pl+pr)>>1;
	build_tree(ls(p),pl,mid);
	build_tree(rs(p),mid+1,pr);
	push_up(p);
}
void add_tag(ll p,ll d){
	tree[p]+=d;
	tag1[p]+=d;
}
void change_tag(ll p,ll x){
	tree[p]=x;                                     //既然是修改,當然是直接覆蓋啦
	tag2[p]=x;                                     
	tag1[p]=0;                                     //再來,tag1不歸零還會出錯
}
void push_down(ll p){                              //先加後改,也會出錯  
	if(tag2[p]!=-inf){                             //注意,tag2陣列的初始值為-inf
		change_tag(ls(p),tag2[p]); 
		change_tag(rs(p),tag2[p]);
		tag2[p]=-inf;                              //用完記得還原
	}
	if(tag1[p]){
		add_tag(ls(p),tag1[p]);
		add_tag(rs(p),tag1[p]);
		tag1[p]=0;
	} 
}
void change(ll l,ll r,ll p,ll pl,ll pr,ll x){
	if(l<=pl&&r>=pr){
		change_tag(p,x);
		return;
	}
	push_down(p);                                  //別忘了下傳標記,下同
	ll mid=(pl+pr)>>1;
	if(l<=mid) change(l,r,ls(p),pl,mid,x);
	if(r>mid) change(l,r,rs(p),mid+1,pr,x);
	push_up(p);                                    //別忘了維護樹,下同
}
void up_data(ll l,ll r,ll p,ll pl,ll pr,ll d){
	if(l<=pl&&r>=pr){
		add_tag(p,d);
		return;
	}
	push_down(p);
	ll mid=(pl+pr)>>1;
	if(l<=mid) up_data(l,r,ls(p),pl,mid,d);
	if(r>mid) up_data(l,r,rs(p),mid+1,pr,d);
	push_up(p);
}
ll query(ll l,ll r,ll p,ll pl,ll pr){
	if(l<=pl&&r>=pr) 
		return tree[p];
	push_down(p);
	ll mid=(pl+pr)>>1,res=-inf;
	if(l<=mid) res=query(l,r,ls(p),pl,mid);
	if(r>mid) res=max(res,query(l,r,rs(p),mid+1,pr));
	return res;
}
ll n,m,op;
ll l,r,sum;
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>m;
	seq(i,1,n){
		cin>>a[i];
	}
	build_tree(1,1,n);                             //不建樹更會報錯
	seq(i,1,m){
		cin>>op>>l>>r;
		if(op==1){
			cin>>sum;
			change(l,r,1,1,n,sum);
		}
		if(op==2){
			cin>>sum;
			up_data(l,r,1,1,n,sum);
		}
		if(op==3){
			cout<<query(l,r,1,1,n)<<"\n";
		}
	}
	return 0;
}

總的來說,是一道不錯的線段樹版子題,不得不說,做完後對線段樹的理解加深了許多。

相關文章