线段树历史区间最值

前情提要

本来是想去打可持久化线段树的,然后发现线段树还有一个类型,就先去打这个了,没想到一打就是一周啊QAQ。

P6242 【模板】线段树 3

  • 1 l r k:对于所有的 i[l,r],将 Ai 加上 kk 可以为负数)。
  • 2 l r v:对于所有的 i[l,r],将 Ai 变成 min(Ai,v)
  • 3 l r:求 i=lrAi
  • 4 l r:对于所有的 i[l,r],求 Ai 的最大值。
  • 5 l r:对于所有的 i[l,r],求 Bi 的最大值。

在每一次操作后,我们都进行一次更新,让 Bimax(Bi,Ai)

重点看第 2 个操作和第 5 个操作。

操作 2

关于第 2 个操作,我们定义 3 个变量来维护:

  • num 表示该区间中最大数的个数。

  • max 表示该区间中的最大数。

  • se 表示该区间中的严格次大数。

这样定义的意义在于当我们在线段树中找到一段区间使得 sevmax,需要更改的数据只有 max 以及 sum

sum(p)=num(p)(max(p)v)

max(p)=v

假设在没有操作 1 的影响下,懒惰标记(addtag),更新操作为以下代码:

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);
继续往下查找。
}
{

但当操作 1 加入进来时,这样更新就行不通了。因为我们不知道此时的最大值是被操作 2 更新变小的,还是操作 1 更新变小的。假设原来原本父区间的最大值是大于子区间的最大值的。在操作 1 的影响下作了减法之后,若此时父区间最大值小于子区间最大值,就会将子区间最大值更新。此时是错误的。

解决方法是将对最大值的更改标记(addmax)和对非最大值(addtag)的更改标记分开来讨论。

对于最大值来说:

  • 操作 1 : addmax(p)+=k

  • 操作 2 : addmax(p)=(max(p)v)

  • spread 时 :

当父区间最大值与子区间最大值相等:max(p2)+=addmax(p) (p2+1 同理)。

否则 : max(p2)+=addtag(p)

更新 sum 时,也将最大值和非最大值的改变分开来讨论。

sum(p2)+=num(p2+1)k1+(r(p)l(p)+1num(p2))addtag(p)

这里的 k1 指的是区间 p2 的最大值的改变,不能直接套 addmax(p),要分类讨论。当子区间最大值等于父区间原本的最大值时,用 addmax(p) 更新,否则就用 addtag(p) 更新。

操作 5

对于实时更新最大值的父区间来说很简单,也可以实时更新历史最大值(hadmax)。但对于被延时更新的子区间来说,不能用最大值加上懒惰标记来更新,因为懒惰标记有可能是结合了好几个 1,2 操作的,需要用到子区间时才统一更新,此时历史最大值有可能在过程中产生

因此我们还需要增加两个变量,一个用来维护 addmax 在过程中的最大值,一个用来维护 addtag 在过程中的最大值。于是更新就成为了:

hadmax(p2)=max(max(p2),max(p2)+k2)

此时保证 max(p2)+k2 是过程中的最大值。

这里的 k2 也要分类讨论子区间最大值是否与父区间原来最大值相同,是 k2 就是 addmax 在过程中的最大值,否则就是 addtag 在过程中的最大值。

代码

#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;
}
posted @   k_stefani  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示