线段树历史问题

纪念创飞我的 D7T1,暂时就写写吉司机和历史和吧。

看例题。

P6242 【模板】线段树 3(区间最值操作、区间历史最值)

给定一个序列 \(a\),定义 \(b_i\)\(a_i\) 的历史最大值,完成一下操作:
1 l r k 表示对 \(i \in [l,r]\),做 \(a_i \gets a_i + k\)
2 l r v 表示对 \(i \in [l,r]\),做 \(a_i \gets \min(a_i,v)\)
3 l r 表示查询 \(\sum \limits_{i=l}^{r} a_i\)
4 l r 表示查询 \(\max \limits_{i=l}^{r} a_i\)
5 l r 表示查询 \(\max \limits_{i=l}^{r} b_i\)
在每一次操作后,对于 \(i \in [1,n]\),都做 \(b_i \gets max(b_i,a_i)\)
\(1 \le n \le 5\times 10^5\)

\(1,3,4\) 操作是简单的,我们先考虑如何做 \(2\) 操作。

对于一个区间内的 \(\le v\) 的数,显然操作后是没有变化的,所以我们只需要记录 \(> v\) 的数即可,但是对每一个节点都开东西存肯定不现实,我们考虑只记录最大值和次大值。

当且仅当这个区间内只更新最大值的时候,我们再更新,这样做的复杂度可以证明是 \(O(n \log^2 n)\) 的,具体来讲,我们只需要对于每一个线段树节点记录最大值,最大值的数量,严格次大值,当进行操作 \(2\) 的时候,一直下放操作,知道区间被 \([l,r]\) 覆盖且 \(maxn < v \leq maxx\) 时再做修改,其中 \(maxn\) 为次大值,\(maxx\) 为最大值。

然后考虑维护历史最大值,对于某个位置 \(i\),其最大的时候肯定是区间加 \(tag\) 最大的时候,因此,我们需要记录 \(tag\) 表示历史最大区间加标记,每次特别维护一下这个标记就好了。

然后我们需要将操作 \(2,5\) 结合起来,具体来讲,我们不能只维护两个 \(tag\),因为我们区分了最大值和非最大值,所以需要给非最大值同样维护两个 \(tag\),含义同上。

总结,我们需要维护 maxx,se,tag1,tag2,tag3,tag4 分别表示区间最大值,严格次大值,最大值标记,非最大值标记,最大值历史标记,非最大值历史标记,然后下放时注意顺序。

这种处理历史问题的线段树被称为吉司机线段树,实现复杂度一般为 \(O(n \log^2 n)\)

ll n,m,a[N];
/*
maxa 最大值
maxb 历史最大值
cnt 最大值个数
tag1,tag2 最大值tag,非最大值tag
tag3,tag4 历史最大值tag,历史非最大值tag
*/
struct SGT{ll l,r,sum,maxa,se,maxb,cnt,tag1,tag2,tag3,tag4;}tree[N<<2];
#define rt tree[root]
#define ls tree[root<<1]
#define rs tree[root<<1|1]
inline void pushup(ll root)
{
    rt.sum=ls.sum+rs.sum,rt.maxa=max(ls.maxa,rs.maxa),rt.maxb=max(ls.maxb,rs.maxb);
    if(ls.maxa==rs.maxa) rt.se=max(ls.se,rs.se),rt.cnt=ls.cnt+rs.cnt;
    if(ls.maxa>rs.maxa) rt.se=max(ls.se,rs.maxa),rt.cnt=ls.cnt;
    if(ls.maxa<rs.maxa) rt.se=max(ls.maxa,rs.se),rt.cnt=rs.cnt;
}
inline void mdf(ll root,ll t1,ll t2,ll t3,ll t4)
{
    rt.sum+=t1*rt.cnt+t2*(rt.r-rt.l+1-rt.cnt);
    rt.maxb=max(rt.maxb,rt.maxa+t3),rt.maxa+=t1,rt.se+=t2;
    rt.tag3=max(rt.tag3,rt.tag1+t3),rt.tag1+=t1;
    rt.tag4=max(rt.tag4,rt.tag2+t4),rt.tag2+=t2;
}
inline void pd(ll root)
{
    ll maxn=max(ls.maxa,rs.maxa),t1=rt.tag1,t2=rt.tag2,t3=rt.tag3,t4=rt.tag4;
    rt.tag1=rt.tag2=rt.tag3=rt.tag4=0;
    if(maxn==ls.maxa) mdf(root<<1,t1,t2,t3,t4);else mdf(root<<1,t2,t2,t4,t4);
    if(maxn==rs.maxa) mdf(root<<1|1,t1,t2,t3,t4);else mdf(root<<1|1,t2,t2,t4,t4);
}
inline void build(ll root,ll l,ll r){rt.l=l,rt.r=r;if(l==r) return rt.sum=rt.maxa=rt.maxb=a[l],rt.se=-INF,rt.cnt=1,void();ll mid=l+r>>1;build(root<<1,l,mid),build(root<<1|1,mid+1,r);pushup(root);}
inline void upd_add(ll root,ll x,ll y,ll k){ll l=rt.l,r=rt.r;if(x<=l&&y>=r) return mdf(root,k,k,k,k),void();ll mid=l+r>>1;pd(root);if(x<=mid) upd_add(root<<1,x,y,k);if(y>mid) upd_add(root<<1|1,x,y,k);pushup(root);}
inline void upd_min(ll root,ll x,ll y,ll v){ll l=rt.l,r=rt.r;if(rt.maxa<=v) return;if(x<=l&&y>=r&&rt.se<v){rt.sum-=rt.cnt*(rt.maxa-v);rt.tag1-=rt.maxa-v;rt.maxa=v;return;}ll mid=l+r>>1;pd(root);if(x<=mid) upd_min(root<<1,x,y,v);if(y>mid) upd_min(root<<1|1,x,y,v);pushup(root);}
inline ll ask_maxa(ll root,ll x,ll y){ll l=rt.l,r=rt.r;if(x<=l&&y>=r) return rt.maxa;ll mid=l+r>>1,ans=-INF;pd(root);if(x<=mid) ans=ask_maxa(root<<1,x,y);if(y>mid) ans=max(ans,ask_maxa(root<<1|1,x,y));return ans;}
inline ll ask_maxb(ll root,ll x,ll y){ll l=rt.l,r=rt.r;if(x<=l&&y>=r) return rt.maxb;ll mid=l+r>>1,ans=-INF;pd(root);if(x<=mid) ans=ask_maxb(root<<1,x,y);if(y>mid) ans=max(ans,ask_maxb(root<<1|1,x,y));return ans;}
inline ll ask_sum(ll root,ll x,ll y){ll l=rt.l,r=rt.r;if(x<=l&&y>=r) return rt.sum;ll mid=l+r>>1,ans=0;pd(root);if(x<=mid) ans=ask_sum(root<<1,x,y);if(y>mid) ans+=ask_sum(root<<1|1,x,y);return ans;}
signed main(){
    read(n),read(m);fo(1,i,n) read(a[i]);build(1,1,n);
    fo(1,i,m)
    {
        ll op,l,r,k;read(op,l,r);
        if(op==1) read(k),upd_add(1,l,r,k);
        if(op==2) read(k),upd_min(1,l,r,k);
        if(op==3) wr(ask_sum(1,l,r)),pr;
        if(op==4) wr(ask_maxa(1,l,r)),pr;
        if(op==5) wr(ask_maxb(1,l,r)),pr;
    }
	return 0;
}
posted @ 2025-05-10 18:51  Wei_Han  阅读(19)  评论(0)    收藏  举报