• 洛谷P4891 序列(势能线段树)


    洛谷题目传送门

    闲话

    考场上一眼看出这是个毒瘤线段树准备杠题,发现实在太难调了,被各路神犇虐哭qwq

    考后看到各种优雅的暴力AC。。。。。。宝宝心里苦qwq

    思路分析

    题面里面是一堆乱七八糟的限制和性质,这时候需要冷静想想有没有可利用的地方。蒟蒻一开始往势能线段树上面想了想。

    定义一个全局势能函数,为所有(C_i<B_i)的位置个数。注意两个操作的修改都不会小于原来的数。

    一个是改(A),相当于对(C)进行区间设置,此时我们每暴力找到一个原来(C_i<B_i)但是现在(C_ige y)的位置,就需要在线段树内跳(log)层,再修改,势能函数就会下降。如果碰到那些修改以后对势能没有影响的子区间,就跳过而不继续暴力递归下去。

    一个是改(B),是单点修改,线段树内跳(log)层,势能函数至多加(1)

    于是,如果我们能够维护信息,从而判断和控制哪里该暴力递归、哪里该跳过的话,我们总的在线段树内跳的次数不会超过((n+q)log n)

    下面用a代替了(C),b代替了(B)。蒟蒻在线段树里维护了:

    • pa:区间所有a<b的位置的a的积
    • pb:区间所有a>=b的位置的b的积
    • ma:区间a的最大值
    • mb:区间所有a<b的位置的b的最小值,没有则设成INF
    • cnt:区间所有a<b的位置的总数
    • la:bool型变量,区间设置懒标记

    修改a的时候,利用单调不降的性质,我们在线段树上先通过二分来对需要修改的若干个子树进行定位。对于当前子区间,如果当前设置值(y)比mb要小,那么设置对答案没有影响,直接打上区间设置标记后退出;否则继续递归直到找到叶子节点,进行修改后退出。

    修改b就比较轻松,只要找到对应的叶子节点改完后一路回溯即可。

    很多事都是说起来容易做起来难,这题也不例外。调试几乎花了整个晚上。要注意的细节很多,也只好自己仔细思考了。

    时间复杂度(O(nlog n+qlog^2n)),上界很松。多出来的(log)是区间改a时需要快速幂更新pa造成的。

    跑了不到200ms,比什么树套树、分块还是要好看一点,但是被暴力碾压也是有点无奈啊~

    #include<bits/stdc++.h>
    #define RG register
    #define R RG int
    #define I inline
    #define G if(++ip==ie)fread(ip=buf,1,N,stdin)
    using namespace std;
    typedef long long LL;
    const LL N=1<<18,YL=1e9+7;
    char buf[N],*ie=buf+N,*ip=ie-1;
    int y,a[N];
    I int in(){
        G;while(*ip<'-')G;
        R x=*ip&15;G;
        while(*ip>'-'){x*=10;x+=*ip&15;G;}
        return x;
    }
    I LL qpow(RG LL b,R k){//快速幂
        RG LL a=1;
        for(;k;k>>=1,(b*=b)%=YL)
            if(k&1)(a*=b)%=YL;
        return a;
    }
    struct Node{//个人认为写指针比较直观(貌似immortalCO也有这样的看法)
        Node*lc,*rc;bool la;
        LL pa,pb;int l,r,m,ma,mb,cnt;
        I void up(){//上传
            pa=lc->pa*rc->pa%YL;
            pb=lc->pb*rc->pb%YL;
            ma=max(lc->ma,rc->ma);
            mb=min(lc->mb,rc->mb);
            cnt=lc->cnt+rc->cnt;
        }
        I void dn(){//下传区间设置标记
            if(la){
                lc->ma=rc->ma=ma;
                lc->la=rc->la=1;la=0;
                lc->pa=qpow(ma,lc->cnt);
                rc->pa=qpow(ma,rc->cnt);
            }
        }
        I void build(R s,R e){//建树
            l=s;r=e;m=(s+e)>>1;la=0;
            if(s==e){
                ma=a[l];mb=in();
                (cnt=ma<mb)?(pa=ma,pb=1):(pa=1,pb=mb,mb=YL);
                return;
            }
            (lc=new Node)->build(l,m);
            (rc=new Node)->build(m+1,r);
            this->up();
        }
        I void upda(){//区间修改a
            if(y<mb){//对区间势能没有影响
                pa=qpow(ma=y,cnt);la=1;
                return;
            }
            if(l==r){//到了叶子节点
                ma=y;pa=1;pb=mb;mb=YL;
                return;
            }
            this->dn();
            lc->upda();rc->upda();
            this->up();
        }
        I void updb(R s){//单点更新b
            if(l==r){//仔细判断三种情况再修改
                if(ma<pb)mb=y;
                else if(ma<y)pa=ma,pb=cnt=1,mb=y;
                else pb=y;
                return;
            }
            this->dn();
            (s<=m?lc:rc)->updb(s);
            this->up();
        }
        I void bound(R s){//线段树二分定位,注意细节
            if(s==l&&ma<y)return this->upda();
            if(l==r)return;
            this->dn();
            if(lc->ma<y)rc->bound(m+1);
            lc->bound(s);
            this->up();
        }
    };
    int main(){
        R n=in(),q=in(),op,x;
        for(R i=1;i<=n;++i)a[i]=max(a[i-1],in());
        RG Node rt;rt.build(1,n);
        while(q--){
            op=in();x=in();y=in();
            op?rt.updb(x):rt.bound(x);
            printf("%lld
    ",rt.pa*rt.pb%YL);
        }
        return 0;
    }
    
  • 相关阅读:
    MODBUS 数据格式相关记录
    STM32Cube基础工程配置
    QT5学习记录(一)
    求最大值
    算法训练方格取数
    传纸条
    分组背包
    混合背包
    二维背包
    多重背包
  • 原文地址:https://www.cnblogs.com/flashhu/p/9697208.html
Copyright © 2020-2023  润新知