III.追想 题解

发布时间 2023-05-22 12:03:33作者: spider_oyster

原题链接

我第一次出的一道比较正经的菜题,欢迎大家来切哦。
感谢魔法少女老干妈 GM_Joanna_ 的支持

对于操作 1,3:

注意到 1e9 的数据至多 5 此操作就能把一个位置变为 0,这个次数可视为常数。
考虑每个位置暴力改,也只会递归 \(5\times n\log n\) 次。
对于 3 操作,考虑最坏的情况,每次把一些数还原成 1e9。
注意到 3 操作后一些数是相同的,那么对于相同的数,就可以直接进行区间 1 操作。
线段树上区间操作复杂度 \(O(\log n)\)
总复杂度 \(O(n\log ^2 n)\)

对于操作 2:

吉司机线段树维护即可。
简略提一下吉司机线段树。

记当前区间最大值为 \(mx\),次大值为 \(se\)。现在有如下三种情况:

  1. \(mx\leq v\),直接返回。
  2. \(se<v \leq mx\),只修改当前区间最大值,可以打标记维护。
  3. \(v \leq se\),暴力递归修改,若在叶子节点,直接修改最大值。

证一下时间复杂度

  • 递归次数与值域(不同数值个数)相关,因为相同的数第二种情况可以直接进行区间操作。
  • 每次递归条件是 \(v \leq se < mx\),则至少会令 \(se=mx=v\),显然值域至少 \(-1\)
  • 不论区间修改还是单点修改,最多只会令值域 \(+1\)
  • 线段树 \(\log n\) 层,每层值域 \(n\),区间修改+单点修改次数 \(n\log n\) 次,故总复杂度 \(O(n\log^2 n)\)

一些细节:

  1. 由于要打区间覆盖和修改最大值的两种标记,要注意下传标记的顺序。
  2. \(\log\) 函数自己手写,用换底公式可能有精度误差。
  3. 暴力进行 \(\log\) 操作到叶子节点时,注意把标记给清空(这鬼地方我调半天)。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=5e5+5;
int n,m,a[N];

struct SegmentTree{
    int mx[N<<2],se[N<<2],c[N<<2],tag[N<<2],tag_mx[N<<2];
    ll sum[N<<2];
    #define lc k<<1
    #define rc k<<1|1
    #define mid ((l+r)>>1)
    inline int max(int x,int y) {return x>y?x:y;}
    inline int log(int x,int k)
    {
        if(x==0) return 0;
        ll res=0,t=1;
        while(t<=x) res++,t*=k;
        return res-1;
    }

    inline void pushup(int k)
    {
        sum[k]=sum[lc]+sum[rc];
        if(mx[lc]==mx[rc])
        {
            mx[k]=mx[lc];
            se[k]=max(se[lc],se[rc]);
            c[k]=c[lc]+c[rc];
        }
        else if(mx[lc]>mx[rc])
        {
            mx[k]=mx[lc];
            se[k]=max(se[lc],mx[rc]);
            c[k]=c[lc];
        }
        else
        {
            mx[k]=mx[rc];
            se[k]=max(mx[lc],se[rc]);
            c[k]=c[rc];
        }
    }

    inline void pushtag(int k,int l,int r,int v)
    {
        tag[k]=mx[k]=v;
        c[k]=r-l+1;
        sum[k]=1ll*(r-l+1)*v;
        se[k]=-1;
    }

    inline void push_mx(int k,int l,int r,int v)
    {
        if(~tag[k])
        {
            if(tag[k]<=v) return;
            tag[k]=mx[k]=v;
            sum[k]=1ll*(r-l+1)*v;
        }
        else
        {
            if(mx[k]<=v) return;
            sum[k]-=1ll*c[k]*(mx[k]-v);
            mx[k]=tag_mx[k]=v;
        }
    }

    inline void pushdown(int k,int l,int r)
    {
        if(~tag[k])
        {
            pushtag(lc,l,mid,tag[k]);
            pushtag(rc,mid+1,r,tag[k]);
            tag[k]=-1,tag_mx[k]=-1;
        }
        if(~tag_mx[k])
        {
            push_mx(lc,l,mid,tag_mx[k]);
            push_mx(rc,mid+1,r,tag_mx[k]);
            tag_mx[k]=-1;
        }
        
    }

    void build(int k=1,int l=1,int r=n)
    {
        tag[k]=tag_mx[k]=-1;
        if(l==r) return sum[k]=mx[k]=a[l],c[k]=1,se[k]=-1,void();
        build(lc,l,mid),build(rc,mid+1,r);
        pushup(k);
    }

    void upd_log(int x,int y,int v,int k=1,int l=1,int r=n)
    {
        if(mx[k]<=0) return;
        if(l>=x&&r<=y&&~tag[k]) return pushtag(k,l,r,log(tag[k],v)),void();
        if(l==r)
        {
            mx[k]=log(mx[k],v);
            sum[k]=mx[k];
            tag[k]=tag_mx[k]=-1;
            return;
        }
        pushdown(k,l,r);
        if(x<=mid) upd_log(x,y,v,lc,l,mid);
        if(mid<y) upd_log(x,y,v,rc,mid+1,r);
        pushup(k);
    }

    void upd_min(int x,int y,int v,int k=1,int l=1,int r=n)
    {
        if(mx[k]<=v) return;
        if(l>=x&&r<=y&&se[k]<v) return push_mx(k,l,r,v);
        pushdown(k,l,r);
        if(x<=mid) upd_min(x,y,v,lc,l,mid);
        if(mid<y) upd_min(x,y,v,rc,mid+1,r);
        pushup(k);
    }

    void upd_cov(int x,int y,int v,int k=1,int l=1,int r=n)
    {
        if(l>=x&&r<=y) return pushtag(k,l,r,v);
        pushdown(k,l,r);
        if(x<=mid) upd_cov(x,y,v,lc,l,mid);
        if(mid<y) upd_cov(x,y,v,rc,mid+1,r);
        pushup(k);
    }

    ll query(int x,int y,int k=1,int l=1,int r=n)
    {
        if(l>=x&&r<=y) return sum[k];
        pushdown(k,l,r);
        ll res=0;
        if(x<=mid) res=query(x,y,lc,l,mid);
        if(mid<y) res+=query(x,y,rc,mid+1,r);
        return res;
    }
}T;

inline int rd()
{
    int x=0;char c=getchar();
    for(;!isdigit(c);c=getchar());
    for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^48);
    return x;
}

int main()
{
    n=rd(),m=rd();
    for(int i=1;i<=n;i++) a[i]=rd();
    T.build();
    while(m--)
    {
        int op=rd(),l=rd(),r=rd(),x;
        if(op!=4) x=rd();
        if(op==1) T.upd_log(l,r,x);
        else if(op==2) T.upd_min(l,r,x);
        else if(op==3) T.upd_cov(l,r,x);
        else printf("%lld\n",T.query(l,r));
    }
}