P1033 · OFFICIAL SOLUTION

P1033 蝶恋花 官方题解

Gioush OJ · P1033 蝶恋花

蝶恋花

【题意简述】

维护序列 aia_i 与历史最大值 bib_i。支持区间加、区间取最小值、查询区间和、查询区间最大值、查询区间历史最大值。

【Hint】

区间取最小值只会影响当前最大值。若一个线段树结点的最大值大于 vv 且严格次大值小于 vv,就可以只修改最大值那一批元素。

【提示】

每个结点维护最大值、严格次大值、最大值个数、区间和与历史最大值。区间加和历史最大值需要用标记同时维护“最大值位置”和“非最大值位置”。

【解法】

这是区间最值操作线段树。每个结点维护:

  • Max\operatorname{Max}:当前最大值;
  • SecMax\operatorname{SecMax}:严格次大值;
  • MaxCnt\operatorname{MaxCnt}:最大值个数;
  • Sum\operatorname{Sum}:区间和;
  • HisMax\operatorname{HisMax}:历史最大值。

对区间取最小值 vv 时:

  • Maxv\operatorname{Max}\le v,没有影响;
  • SecMax<v<Max\operatorname{SecMax}<v<\operatorname{Max},只把最大值部分压到 vv,可以整段打标记;
  • 否则继续递归。

区间加会同时改变最大值与非最大值;历史最大值要求标记记录当前值变化与历史变化,因此代码中将最大值位置和其他位置分开维护。

【标记复合】

Data 维护区间长度、区间和、最大值、严格次大值、最大值个数与区间历史最大值。标记将元素分为“当前最大值”和“其余元素”两组,分别维护最终增加量与过程中的最大前缀增加量。

规定 ABA\circ B 表示先执行 BB 再执行 AA。以最大值组为例:

MaxAddAB=MaxAddB+MaxAddA,MaxHisAB=max(MaxHisB,MaxAddB+MaxHisA).\begin{aligned} \operatorname{MaxAdd}_{A\circ B}&=\operatorname{MaxAdd}_B+\operatorname{MaxAdd}_A,\\ \operatorname{MaxHis}_{A\circ B}&=\max\left(\operatorname{MaxHis}_B,\operatorname{MaxAdd}_B+\operatorname{MaxHis}_A\right). \end{aligned}

区间加对应两组同时增加;当 SecMax<v<Max\operatorname{SecMax}<v<\operatorname{Max} 时,区间取最小值只使最大值组下降。不能整段执行时递归,沿用 Segment Tree Beats 的势能分析。

【复杂度】

均摊复杂度为 O((n+q)log2n)\mathcal{O}((n+q)\log^2 n),空间复杂度为 O(n)\mathcal{O}(n)

【参考代码】

/*Author:EhundateghDate:2026/7/23Name:flutter.cppYou steal,I kill.*/#include <cstdio>#include <cstring>#include <algorithm>#define MAXN 500010#define LSon Node[Now].LeftSon#define RSon Node[Now].RightSonusing namespace std;const int INF=2000000000; int n,m,In1,In2,In3,cnt=0,Line[MAXN]; struct Data{    int Len,Max,SecMax,HisMax,MaxCnt;    long long Sum;}; struct tag{    int MaxAdd,OtherAdd,MaxHis,OtherHis;}; tag Empty(){return {0,0,0,0};}Data Null(){return {0,-INF,-INF,-INF,0,0};} Data operator+(const Data &A,const Data &B){    if(!A.Len) return B;    if(!B.Len) return A;    Data Ret={A.Len+B.Len,0,0,max(A.HisMax,B.HisMax),0,A.Sum+B.Sum};    if(A.Max==B.Max) Ret.Max=A.Max,Ret.MaxCnt=A.MaxCnt+B.MaxCnt,Ret.SecMax=max(A.SecMax,B.SecMax);    else if(A.Max>B.Max) Ret.Max=A.Max,Ret.MaxCnt=A.MaxCnt,Ret.SecMax=max(A.SecMax,B.Max);    else Ret.Max=B.Max,Ret.MaxCnt=B.MaxCnt,Ret.SecMax=max(A.Max,B.SecMax);    return Ret;} Data operator*(const tag &Tag,const Data &B){    Data Ret=B;    Ret.HisMax=max(Ret.HisMax,Ret.Max+Tag.MaxHis);    Ret.Sum+=1ll*Tag.MaxAdd*Ret.MaxCnt+1ll*Tag.OtherAdd*(Ret.Len-Ret.MaxCnt);    Ret.Max+=Tag.MaxAdd;    if(Ret.SecMax!=-INF) Ret.SecMax+=Tag.OtherAdd;    return Ret;} tag operator*(const tag &A,const tag &B){    tag Ret=B;    Ret.MaxHis=max(Ret.MaxHis,Ret.MaxAdd+A.MaxHis);    Ret.OtherHis=max(Ret.OtherHis,Ret.OtherAdd+A.OtherHis);    Ret.MaxAdd+=A.MaxAdd;Ret.OtherAdd+=A.OtherAdd;    return Ret;} struct node{    int l,r,LeftSon,RightSon;    Data Val;    tag Tag;}Node[MAXN<<2]; void Update(int Now){Node[Now].Val=Node[LSon].Val+Node[RSon].Val;} void Mark(int Now,tag Tag){    Node[Now].Val=Tag*Node[Now].Val;    Node[Now].Tag=Tag*Node[Now].Tag;    return;} void PushDown(int Now){    tag Tag=Node[Now].Tag;    if(!Tag.MaxAdd&&!Tag.OtherAdd&&!Tag.MaxHis&&!Tag.OtherHis) return;    int Max=max(Node[LSon].Val.Max,Node[RSon].Val.Max);    tag Other={Tag.OtherAdd,Tag.OtherAdd,Tag.OtherHis,Tag.OtherHis};    if(Node[LSon].Val.Max==Max) Mark(LSon,Tag);else Mark(LSon,Other);    if(Node[RSon].Val.Max==Max) Mark(RSon,Tag);else Mark(RSon,Other);    Node[Now].Tag=Empty();    return;} int Build(int l,int r){    int Now=++cnt;    Node[Now]={l,r,0,0,{r-l+1,-INF,-INF,-INF,0,0},Empty()};    if(l==r){Node[Now].Val={1,Line[l],-INF,Line[l],1,Line[l]};return Now;}    int Mid=(l+r)>>1;    LSon=Build(l,Mid);    RSon=Build(Mid+1,r);    Update(Now);    return Now;} void Modify(int Now,int l,int r,tag Tag){    if(Node[Now].l>=l&&Node[Now].r<=r){Mark(Now,Tag);return;}    else if(Node[Now].l>r||Node[Now].r<l) return;    PushDown(Now);    Modify(LSon,l,r,Tag);Modify(RSon,l,r,Tag);    Update(Now);    return;} void Limit(int Now,int l,int r,int Val){    if(Node[Now].l>r||Node[Now].r<l||Node[Now].Val.Max<=Val) return;    if(Node[Now].l>=l&&Node[Now].r<=r&&Node[Now].Val.SecMax<Val){        Mark(Now,{Val-Node[Now].Val.Max,0,0,0});        return;    }    PushDown(Now);    Limit(LSon,l,r,Val);Limit(RSon,l,r,Val);    Update(Now);    return;} Data Query(int Now,int l,int r){    if(Node[Now].l>=l&&Node[Now].r<=r) return Node[Now].Val;    else if(Node[Now].l>r||Node[Now].r<l) return Null();    PushDown(Now);    return Query(LSon,l,r)+Query(RSon,l,r);} int main(){    freopen("flutter.in","r",stdin);    freopen("flutter.out","w",stdout);    int c,T;    scanf("%d%d",&c,&T);    while(T-->0){        scanf("%d%d",&n,&m);        cnt=0;        for(int i=1;i<=n;i++) scanf("%d",&Line[i]);        Build(1,n);        while(m-->0){            scanf("%d%d%d",&In1,&In2,&In3);            if(In1==1){scanf("%d",&In1);Modify(1,In2,In3,{In1,In1,In1,In1});}            else if(In1==2){scanf("%d",&In1);Limit(1,In2,In3,In1);}            else if(In1==3) printf("%lld\n",Query(1,In2,In3).Sum);            else if(In1==4) printf("%d\n",Query(1,In2,In3).Max);            else printf("%d\n",Query(1,In2,In3).HisMax);        }    }    return 0;}