做了我好长时间啊,这个线段树还是很经典的
题意:
四种操作,题目写的很清楚,就不说了,主要是这个题目对于操作2 3 用lazy标记很巧妙
题解:
线段树,lazy标记升级
void pushDown(int p)
{
if(lazy[p])
{
lazy[p<<1]+=lazy[p];
lazy[p<<1|1]+=lazy[p];
sum[p<<1]+=lazy[p];
sum[p<<1|1]+=lazy[p];
mins[p<<1]+=lazy[p];
mins[p<<1|1]+=lazy[p];
lazy[p]=0;
}
sum[p<<1|1]=min(sum[p<<1|1],sum[p]);
sum[p<<1|1]=max(sum[p<<1|1],mins[p]);
mins[p<<1|1]=max(mins[p<<1|1],mins[p]);
mins[p<<1|1]=min(mins[p<<1|1],sum[p]);
sum[p<<1]=min(sum[p<<1],sum[p]);
sum[p<<1]=max(sum[p<<1],mins[p]);
mins[p<<1]=max(mins[p<<1],mins[p]);
mins[p<<1]=min(mins[p<<1],sum[p]);
}
后面的那8个式子子,是向下传递最大最小值
若父亲节点的最大值是max 最小值是min,那么子节点的最大最小值一定在min~max范围内
比如说:子节点的最小值要么等于min,要么大于min,若大于min的时候就要注意了,不能大于父亲节点的最大值
满足这些条件就可以得到一个完美的lazy标记的pushDown函数了
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define MAXN 200010
#define lson l,mid,p<<1
#define rson mid+1,r,p<<1|1
#define mem(a) memset(a,0,sizeof(a[0]))
#define LL long long
LL min(LL x,LL y){return x<y?x:y;}
LL max(LL x,LL y){return x>y?x:y;}
LL sum[MAXN<<2];
LL mins[MAXN<<2];
LL lazy[MAXN<<2];
void pushUp(int p)
{
sum[p]=max(sum[p<<1],sum[p<<1|1]);
mins[p]=min(mins[p<<1],mins[p<<1|1]);
}
void pushDown(int p)
{
if(lazy[p])
{
lazy[p<<1]+=lazy[p];
lazy[p<<1|1]+=lazy[p];
sum[p<<1]+=lazy[p];
sum[p<<1|1]+=lazy[p];
mins[p<<1]+=lazy[p];
mins[p<<1|1]+=lazy[p];
lazy[p]=0;
}
sum[p<<1|1]=min(sum[p<<1|1],sum[p]);
sum[p<<1|1]=max(sum[p<<1|1],mins[p]);
mins[p<<1|1]=max(mins[p<<1|1],mins[p]);
mins[p<<1|1]=min(mins[p<<1|1],sum[p]);
sum[p<<1]=min(sum[p<<1],sum[p]);
sum[p<<1]=max(sum[p<<1],mins[p]);
mins[p<<1]=max(mins[p<<1],mins[p]);
mins[p<<1]=min(mins[p<<1],sum[p]);
}
void build(int l,int r,int p)
{
lazy[p]=0;///重要,不知道memset和这个有什么区别
if(l==r){
scanf("%I64d",&sum[p]);
mins[p]=sum[p];
return;
}
int mid=(l+r)>>1;
build(lson);
build(rson);
pushUp(p);
}
LL ans_max,ans_min;
void query(int l,int r,int p,LL i,LL j)
{
if(l>=i&&r<=j){
ans_max=max(ans_max,sum[p]);
ans_min=min(ans_min,mins[p]);
return ;
}
pushDown(p);///防止有值没有传下去
int mid=(l+r)>>1;
if(mid>=i) query(lson,i,j);
if(mid<j) query(rson,i,j);
pushUp(p);
}
void update_1(int l,int r,int p,LL i,LL j,LL num)
{
if(l>=i&&r<=j){
sum[p]+=num;
mins[p]+=num;
lazy[p]+=num;
return ;
}
pushDown(p);
int mid=(l+r)>>1;
if(mid>=i)
update_1(lson,i,j,num);
if(mid<j)
update_1(rson,i,j,num);
pushUp(p);
}
void update_2(int l,int r,int p,LL i,LL j,LL num)
{
if(l>=i&&r<=j){
sum[p]=min(sum[p],num);
mins[p]=min(mins[p],num);
return ;
}
pushDown(p);
int mid=(l+r)>>1;
if(mid>=i)
update_2(lson,i,j,num);
if(mid<j)
update_2(rson,i,j,num);
pushUp(p);
}
void update_3(int l,int r,int p,LL i,LL j,LL num)
{
if(l>=i&&r<=j){
sum[p]=max(sum[p],num);
mins[p]=max(mins[p],num);
return ;
}
pushDown(p);
int mid=(l+r)>>1;
if(mid>=i)
update_3(lson,i,j,num);
if(mid<j)
update_3(rson,i,j,num);
pushUp(p);
}
int main()
{
int n,q,T;
//freopen("in.txt","r",stdin);
scanf("%d",&T);
while(T--)
{
mem(sum);
mem(mins);
scanf("%d%d",&n,&q);
build(1,n,1);
LL temp,a,b,c;
while(q--)
{
scanf("%I64d",&temp);
if(temp==1){
scanf("%I64d%I64d%I64d",&a,&b,&c);
update_1(1,n,1,a,b,c);
}else if(temp==2){
scanf("%I64d%I64d%I64d",&a,&b,&c);
update_2(1,n,1,a,b,c);
}else if(temp==3){
scanf("%I64d%I64d%I64d",&a,&b,&c);
update_3(1,n,1,a,b,c);
}else{
scanf("%I64d%I64d",&a,&b);
ans_max=-0x3f3f3f3f3f3f;
ans_min=0x3f3f3f3f3f3f;
query(1,n,1,a,b);
printf("%I64d %I64d\n",ans_min,ans_max);
}
}
}
return 0;
}

这篇博客介绍了XTU OJ 1238题目的线段树解法,重点讨论了如何使用lazy标记处理操作2和3的巧妙之处。博主详细解释了线段树的lazy标记升级,并给出了关键的pushDown函数实现,确保在更新子节点时保持最大值和最小值的正确范围。
1861

被折叠的 条评论
为什么被折叠?



