线段树历史区间最值
前情提要
本来是想去打可持久化线段树的,然后发现线段树还有一个类型,就先去打这个了,没想到一打就是一周啊QAQ。
P6242 【模板】线段树 3
1 l r k
:对于所有的 ,将 加上 ( 可以为负数)。2 l r v
:对于所有的 ,将 变成 。3 l r
:求 。4 l r
:对于所有的 ,求 的最大值。5 l r
:对于所有的 ,求 的最大值。
在每一次操作后,我们都进行一次更新,让 。
重点看第 个操作和第 个操作。
操作
关于第 个操作,我们定义 个变量来维护:
-
表示该区间中最大数的个数。
-
表示该区间中的最大数。
-
表示该区间中的严格次大数。
这样定义的意义在于当我们在线段树中找到一段区间使得 ,需要更改的数据只有 以及 。
假设在没有操作 的影响下,懒惰标记(),更新操作为以下代码:
void addtag(int p,int v){ if(v>=max_dat(p)) return; //说明此时该区间没有被更新的必要。 //可能是他的父区间没有被更新。 //也可能父区间的最大值不在该区间中。 sum(p)-=num(p)*(max_dat(p)-v); max_dat(p)=v; return; } void spread(int p){ addtag(p*2,max(p)); addtag(p*2+1,max(p)); return; } if(type==2){ if(v>=max(p)) return;//此时没有更新的必要 else if(v>=se(p)){ //此时只需要更新最大值 addtag(p,v); return; } else{ spread(p); 继续往下查找。 } {
但当操作 加入进来时,这样更新就行不通了。因为我们不知道此时的最大值是被操作 更新变小的,还是操作 更新变小的。假设原来原本父区间的最大值是大于子区间的最大值的。在操作 的影响下作了减法之后,若此时父区间最大值小于子区间最大值,就会将子区间最大值更新。此时是错误的。
解决方法是将对最大值的更改标记()和对非最大值()的更改标记分开来讨论。
对于最大值来说:
-
操作 :
-
操作 :
-
时 :
当父区间最大值与子区间最大值相等: ( 同理)。
否则 :
更新 时,也将最大值和非最大值的改变分开来讨论。
这里的 指的是区间 的最大值的改变,不能直接套 ,要分类讨论。当子区间最大值等于父区间原本的最大值时,用 更新,否则就用 更新。
操作
对于实时更新最大值的父区间来说很简单,也可以实时更新历史最大值()。但对于被延时更新的子区间来说,不能用最大值加上懒惰标记来更新,因为懒惰标记有可能是结合了好几个 操作的,需要用到子区间时才统一更新,此时历史最大值有可能在过程中产生。
因此我们还需要增加两个变量,一个用来维护 在过程中的最大值,一个用来维护 在过程中的最大值。于是更新就成为了:
此时保证 是过程中的最大值。
这里的 也要分类讨论子区间最大值是否与父区间原来最大值相同,是 就是 在过程中的最大值,否则就是 在过程中的最大值。
代码
#include<iostream> #include<cstdio> #include<cmath> #define min_num -0x7FFFFFFF #define ll long long using namespace std; ll n,m; struct node{ ll l,r,max_dat,had_max,se_dat,add_max,max_tag,add_tag,num,max_add_tag; ll sum; #define l(x) t[x].l #define r(x) t[x].r #define sum(x) t[x].sum #define max_dat(x) t[x].max_dat #define se_dat(x) t[x].se_dat #define add_max(x) t[x].add_max #define max_tag(x) t[x].max_tag//addmax 过程中最大值 #define add_tag(x) t[x].add_tag #define max_add_tag(x) t[x].max_add_tag//addtag 过程中最大值 #define had_max(x) t[x].had_max #define num(x) t[x].num }t[500005*4]; ll a[500005]; ll ans_sum,max_ans; void replace(ll p){//通过子区间更新父区间 max_dat(p)=max(max_dat(p*2),max_dat(p*2+1)); had_max(p)=max(had_max(p*2),had_max(p*2+1)); sum(p)=sum(p*2)+sum(p*2+1); if(max_dat(p*2)==max_dat(p*2+1)){ se_dat(p)=max(se_dat(p*2+1),se_dat(p*2)); num(p)=num(p*2)+num(p*2+1); } else{ se_dat(p)=max(se_dat(p*2),se_dat(p*2+1)); se_dat(p)=max(se_dat(p),min(max_dat(p*2),max_dat(p*2+1))); if(max_dat(p*2)>max_dat(p*2+1)) num(p)=num(p*2); else num(p)=num(p*2+1); } return; } void build(ll p,ll l,ll r){ l(p)=l,r(p)=r; if(l==r){ had_max(p)=sum(p)=max_dat(p)=a[l]; se_dat(p)=min_num; num(p)=1; return; } ll mid=(l+r)/2; build(p*2,l,mid); build(p*2+1,mid+1,r); had_max(p)=min_num; replace(p); return; } void spread(ll p,ll k1,ll k2,ll k3,ll k4){ //k1k2与最大值更改相关,k3k4则是与非最大值更改相关 sum(p)+=k1*num(p)+k3*(r(p)-l(p)+1-num(p)); //一定要先更新历史最大值,再更新最大值 had_max(p)=max(had_max(p),max_dat(p)+k2); max_dat(p)+=k1; if(se_dat(p)!=min_num) se_dat(p)+=k3; //标记更新也要注意顺序! max_tag(p)=max(max_tag(p),add_max(p)+k2),add_max(p)+=k1; max_add_tag(p)=max(max_add_tag(p),add_tag(p)+k4),add_tag(p)+=k3; return; } void ask(ll p){ ll maxn=max(max_dat(p*2),max_dat(p*2+1)); //最大值分类讨论是否与原本父区间最大值相同 if(maxn==max_dat(p*2)) spread(p*2,add_max(p),max_tag(p),add_tag(p),max_add_tag(p)); else spread(p*2,add_tag(p),max_add_tag(p),add_tag(p),max_add_tag(p)); if(maxn==max_dat(p*2+1)) spread(p*2+1,add_max(p),max_tag(p),add_tag(p),max_add_tag(p)); else spread(p*2+1,add_tag(p),max_add_tag(p),add_tag(p),max_add_tag(p)); add_max(p)=max_tag(p)=add_tag(p)=max_add_tag(p)=0; return; } void f(ll p,ll l,ll r,ll type,ll num){ if(l<=l(p)&&r>=r(p)){ if(type==1){ sum(p)+=(r(p)-l(p)+1)*num; add_tag(p)+=num; add_max(p)+=num; max_dat(p)+=num; if(se_dat(p)!=min_num) se_dat(p)+=num; had_max(p)=max(had_max(p),max_dat(p)); max_add_tag(p)=max(max_add_tag(p),add_tag(p)); max_tag(p)=max(max_tag(p),add_max(p)); return; } if(type==2){ if(num>max_dat(p)) return; else if(num>=se_dat(p)){ ll k=max_dat(p)-num; add_max(p)-=k; sum(p)-=k*num(p); max_dat(p)=num; return; } else{ ask(p); ll mid=(l(p)+r(p))/2; if(l<=mid) f(p*2,l,r,type,num); if(r>mid) f(p*2+1,l,r,type,num); replace(p); return; } } if(type==3){ ans_sum+=sum(p); return; } if(type==4){ max_ans=max(max_ans,max_dat(p)); return; } if(type==5){ max_ans=max(max_ans,had_max(p)); return; } } ask(p); ll mid=(r(p)+l(p))/2; if(l<=mid) f(p*2,l,r,type,num); if(r>mid) f(p*2+1,l,r,type,num); replace(p); return; } int main(){ //freopen("P6242_6.in","r",stdin); //freopen("ans.txt","w",stdout); scanf("%lld%lld",&n,&m); for(ll i=1;i<=n;i++) scanf("%lld",&a[i]); build(1,1,n); for(ll i=1;i<=m;i++){ ll type,l,r; scanf("%lld%lld%lld",&type,&l,&r); // cout<<i<<" "<<type<<" "<<l<<" "<<r<<endl; if(type==1){ ll k; scanf("%lld",&k); f(1,l,r,type,k); } if(type==2){ ll v; scanf("%lld",&v); f(1,l,r,type,v); } if(type==3){ ans_sum=0; f(1,l,r,type,0); cout<<ans_sum<<endl; } if(type==4||type==5){ //cout<<min_num<<endl; max_ans=min_num; f(1,l,r,type,0); cout<<max_ans<<endl; } } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!