【学习笔记】摸鱼之 - 吉老师线段树
- 这是吉老师在
2016 年国家集训队论文中提到的线段树处理历史区间最值的问题。
我们先看一道例题:
大致题面
- 给你一个序列
A,要支持以下操作:
0 l r val
: 区间
[l,r]与
val取最小值
1 l r
:询问
maxi=lr
2 l r
:询问
∑i=lrAi
Sol
- 我们就设
mx,mx2,cmx分别为区间最大值,次大值,最大值的个数
- 我们考虑区间取
min得过程,就是把
mx=val的过程。那么我们分情况考虑:
- 如果当
mx≤val显然对原序列不会产生影响
- 如果当
mx2≤val≤mx,那么此时区间和
sum只要减去
cmx×(mx−val)即可,这很有道理吧!
- 否则我们就只能暴力向下做然后
pushup啦。
- 这个流程应该还是比较清楚的,根据什么势能分析可知复杂度为
O(mlogn)(我不会证)
然后就是一道众所周知的卡常题目
- 我至今还没有卡过点
4(正确性没问题,就是链比较黑了。。。
大致题面
Sol
- 这是一道各种操作一起来的题目。
- 我们像例题一样,设
mx,mx2,cmx分别为区间最大值,次大值,最大值的个数,最小值同理。以及
sum区间和,
tag,tagmx,tagmi一系列懒标记。
- 然后就是像例题一样的大力
pushup
- 重要的是那些最大,最小值修改(我们那修改最小值来说吧:当我们该区间最小值时还应该考虑去修改区间最大值,即如果
val≤tagmx,那么说有的数都要变成
val以及
tagmx也要变成
val。最大值修改同理。
- 维护的时候,区间加优于区间最值问题。
Code
- 下面代码时会
TLE的,求那位神仙帮我卡卡常(我已经用了众所周知的卡常方法。
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#include <bits/stdc++.h>
#define pb push_back
#define int long long
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline int read()
{
int sum=0,ff=1; char ch=gc();
while(!isdigit(ch))
{
if(ch=='-') ff=-1;
ch=gc();
}
while(isdigit(ch))
sum=sum*10+(ch^48),ch=gc();
return sum*ff;
}
const int N=5e5+5;
const int INF=1e9;
int n,m,a[N];
struct Seg
{
struct nood
{
int mx,mx2,mn,mn2;
int cmx,cmn,tmx,tmn,tag;
int sum;
};
nood T[N*4];
inline int min(int i,int j)
{
if(i<j) return i;
return j;
}
inline int max(int i,int j)
{
if(i>j) return i;
return j;
}
inline void push_up(int rt)
{
int ls=rt<<1,rs=rt<<1|1;
T[rt].sum=T[ls].sum+T[rs].sum;
if(T[ls].mx==T[rs].mx)
{
T[rt].mx=T[ls].mx;
T[rt].cmx=T[ls].cmx+T[rs].cmx;
T[rt].mx2=max(T[ls].mx2,T[rs].mx2);
}
else if(T[ls].mx<T[rs].mx)
{
T[rt].mx=T[rs].mx;
T[rt].cmx=T[rs].cmx;
T[rt].mx2=max(T[rs].mx2,T[ls].mx);
}
else if(T[ls].mx>T[rs].mx)
{
T[rt].mx=T[ls].mx;
T[rt].cmx=T[ls].cmx;
T[rt].mx2=max(T[ls].mx2,T[rs].mx);
}
if(T[ls].mn==T[rs].mn)
{
T[rt].mn=T[ls].mn;
T[rt].cmn=T[ls].cmn+T[rs].cmn;
T[rt].mn2=min(T[ls].mn2,T[rs].mn2);
}
else if(T[ls].mn<T[rs].mn)
{
T[rt].mn=T[ls].mn;
T[rt].cmn=T[ls].cmn;
T[rt].mn2=min(T[ls].mn2,T[rs].mn);
}
else if(T[ls].mn>T[rs].mn)
{
T[rt].mn=T[rs].mn;
T[rt].cmn=T[rs].cmn;
T[rt].mn2=min(T[ls].mn,T[rs].mn2);
}
}
inline void pd_add(int rt,int l,int r,int val)
{
T[rt].sum+=(r-l+1)*val;
T[rt].mx+=val;
T[rt].mn+=val;
if(T[rt].mn2!=INF) T[rt].mn2+=val;
if(T[rt].mx2!=-INF) T[rt].mx2+=val;
if(T[rt].tmn!=INF) T[rt].tmn+=val;
if(T[rt].tmx!=-INF) T[rt].tmx+=val;
T[rt].tag+=val;
}
inline void pd_MAX(int rt,int val)
{
if(T[rt].mn>=val) return;
T[rt].sum+=(val-T[rt].mn)*T[rt].cmn;
if(T[rt].mx2==T[rt].mn) T[rt].mx2=val;
if(T[rt].mx==T[rt].mn) T[rt].mx=val;
if(T[rt].tmn<val) T[rt].tmn=val;
T[rt].mn=val;
T[rt].tmx=val;
}
inline void pd_MIN(int rt,int val)
{
if(T[rt].mx<=val) return;
T[rt].sum+=(val-T[rt].mx)*T[rt].cmx;
if(T[rt].mn2==T[rt].mx) T[rt].mn2=val;
if(T[rt].mn==T[rt].mx) T[rt].mn=val;
if(T[rt].tmx>val) T[rt].tmx=val;
T[rt].mx=val;
T[rt].tmn=val;
}
inline void pd(int rt,int l,int r)
{
int mid=(l+r)/2;
if(T[rt].tag)
{
pd_add(rt<<1,l,mid,T[rt].tag);
pd_add(rt<<1|1,mid+1,r,T[rt].tag);
}
if(T[rt].tmn!=INF)
{
pd_MIN(rt<<1,T[rt].tmn);
pd_MIN(rt<<1|1,T[rt].tmn);
}
if(T[rt].tmx!=-INF)
{
pd_MAX(rt<<1,T[rt].tmx);
pd_MAX(rt<<1|1,T[rt].tmx);
}
T[rt].tag=0,T[rt].tmn=INF,T[rt].tmx=-INF;
}
inline void build(int rt,int l,int r)
{
T[rt].tmx=-INF,T[rt].tmn=INF;
if(l==r)
{
T[rt].sum=T[rt].mx=T[rt].mn=a[l];
T[rt].mn2=INF,T[rt].mx2=-INF;
T[rt].cmn=1,T[rt].cmx=1;
return;
}
int mid=(l+r)/2;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
push_up(rt);
}
inline void add(int rt,int l,int r,int ll,int rr,int val)
{
if(rr<l||ll>r) return;
if(ll<=l&&r<=rr)
{
pd_add(rt,l,r,val);
return;
}
pd(rt,l,r);
int mid=(l+r)/2;
if(ll<=mid) add(rt<<1,l,mid,ll,rr,val);
if(rr>mid) add(rt<<1|1,mid+1,r,ll,rr,val);
push_up(rt);
}
inline void MIN(int rt,int l,int r,int ll,int rr,int val)
{
if(rr<l||ll>r) return;
if(T[rt].mx<=val) return;
if(ll<=l&&r<=rr&&T[rt].mx2<val)
{
pd_MIN(rt,val);
return;
}
int mid=(l+r)/2;
pd(rt,l,r);
if(ll<=mid) MIN(rt<<1,l,mid,ll,rr,val);
if(rr>mid) MIN(rt<<1|1,mid+1,r,ll,rr,val);
push_up(rt);
}
inline void MAX(int rt,int l,int r,int ll,int rr,int val)
{
if(rr<l||ll>r) return;
if(T[rt].mn>=val) return;
if(ll<=l&&r<=rr&&T[rt].mn2>val)
{
pd_MAX(rt,val);
return;
}
int mid=(l+r)/2;
pd(rt,l,r);
if(ll<=mid) MAX(rt<<1,l,mid,ll,rr,val);
if(rr>mid) MAX(rt<<1|1,mid+1,r,ll,rr,val);
push_up(rt);
}
inline long long ask_add(int rt,int l,int r,int ll,int rr)
{
if(rr<l||ll>r) return 0ll;
if(ll<=l&&r<=rr) return T[rt].sum;
int mid=(l+r)/2,sum=0;
pd(rt,l,r);
if(ll<=mid) sum+=ask_add(rt<<1,l,mid,ll,rr);
if(rr>mid) sum+=ask_add(rt<<1|1,mid+1,r,ll,rr);
return sum;
}
inline int ask_MAX(int rt,int l,int r,int ll,int rr)
{
if(rr<l||ll>r) return -INF;
if(ll<=l&&r<=rr) return T[rt].mx;
int mid=(l+r)/2,mx=-INF;
pd(rt,l,r);
if(ll<=mid) mx=max(mx,ask_MAX(rt<<1,l,mid,ll,rr));
if(rr>mid) mx=max(mx,ask_MAX(rt<<1|1,mid+1,r,ll,rr));
return mx;
}
inline int ask_MIN(int rt,int l,int r,int ll,int rr)
{
if(rr<l||ll>r) return INF;
if(ll<=l&&r<=rr) return T[rt].mn;
int mid=(l+r)/2,mn=INF;
pd(rt,l,r);
if(ll<=mid) mn=min(mn,ask_MIN(rt<<1,l,mid,ll,rr));
if(rr>mid) mn=min(mn,ask_MIN(rt<<1|1,mid+1,r,ll,rr));
return mn;
}
};
Seg T;
signed main()
{
n=read();
for ( register int i=1;i<=n;i++ ) a[i]=read();
T.build(1,1,n);
m=read();
for ( register int i=1;i<=n;++i )
{
int op,l,r,x;
op=read(),l=read(),r=read();
if(op<=3) x=read();
if(op==1) T.add(1,1,n,l,r,x);
if(op==2) T.MAX(1,1,n,l,r,x);
if(op==3) T.MIN(1,1,n,l,r,x);
if(op==4) printf("%lld\n",T.ask_add(1,1,n,l,r));
if(op==5) printf("%lld\n",T.ask_MAX(1,1,n,l,r));
if(op==6) printf("%lld\n",T.ask_MIN(1,1,n,l,r));
}
return 0;
}
历史最值问题(先鸽了。。