把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

题解 P4041 [AHOI2014/JSOI2014] 奇怪的计算器

洛谷

题意

有 $Q$ 个询问,$n$ 个操作,一共有 4 种:

  1. $+a$:结果加上 $a$;
  2. $-a$:结果减去 $a$;
  3. $\times a$;结果乘上 $a$;
  4. $@a$:结果加上 $a\times X$($X$ 是一开始输入的数)。 将每个询问放在 $n$ 个操作中过一遍,同时每次处理完后,倘若小于 $L$,赋值为 $L$;倘若大于 $R$,赋值为 $R$。

暴力

按照题意模拟即可。

分析

我们可以发现,我们所有数的经历的过程是一样的,我们可以用线段树来维护我们的 6 中操作:

  1. 区间加;
  2. 区间减(同 1);
  3. 区间乘;
  4. 区间加的变种;
  5. 区间取至最大值;
  6. 区间取至最小值。

这么算来,我们似乎是要维护 5 种 tag,10 种值。
想想都让人头皮发麻,怎么都应该是黑题才对。

但是实际上,我们的其中操作其实并不会改变期间相邻数的大小关系,因此,我们的 5、6 操作就变成了区间覆盖与一个二分(找到最后一个小于 $L$ 或 第一个大于 $R$)啦。

总结一下,我们需要维护区间乘、区间加、区间加变形、区间覆盖、单点查询。

看起来还是非常复杂,但是我们用 $c_p\times tag1_p+tag2_p$ 的形式,就可以将区间乘,区间加,区间覆盖融为一体。
最后的区间加变种,地位与区间加等同,与区间加的维护大致相同。

struct SEG {
#define ls p<<1
#define rs p<<1|1
    int c[N<<2],tag[N<<2],tag2[N<<2],tag3[N<<2];

    inline void cl(int p,int x,int y,int L,int R) {
        tag3[p]*=x;
        tag2[p]=tag2[p]*x+y;
        tag[p]=tag[p]*x;
    }

    inline void cl(int p,int x) {
        tag3[p]+=x;
    }

    inline void pushdown(int p,int L,int R) {
        int mid=L+R>>1;
        cl(ls,tag[p],tag2[p],L,mid),cl(rs,tag[p],tag2[p],mid+1,R);
        cl(ls,tag3[p]),cl(rs,tag3[p]);
        tag[p]=1,tag2[p]=tag3[p]=0;
    }

    inline void build(int p,int L,int R) {
        tag[p]=1,tag2[p]=tag3[p]=0;
        if(L==R) {
            c[p]=qu[L].x;
            return ;
        }
        int mid=L+R>>1;
        build(ls,L,mid),build(rs,mid+1,R);
    }

    inline void change(int p,int L,int R,int l,int r,int x,int y) {
        if(l>r) return ;
        if(l<=L&&R<=r) {
            cl(p,x,y,L,R);
            return ;
        }
        int mid=L+R>>1;
        pushdown(p,L,R);
        if(l<=mid) change(ls,L,mid,l,r,x,y);
        if(mid<r)  change(rs,mid+1,R,l,r,x,y);
    }

    inline void change(int p,int L,int R,int l,int r,int x) {
        if(l>r) return ;
        if(l<=L&&R<=r) {
            cl(p,x);
            return ;
        }
        int mid=L+R>>1;
        pushdown(p,L,R);
        if(l<=mid) change(ls,L,mid,l,r,x);
        if(mid<r)  change(rs,mid+1,R,l,r,x);
    }

    inline int query(int p,int L,int R,int x) {
        if(L==R) return c[p]*tag[p]+tag2[p]+tag3[p]*c[p];
        pushdown(p,L,R);
        int mid=L+R>>1;
        if(x<=mid) return query(ls,L,mid,x);
        if(mid<x)  return query(rs,mid+1,R,x);
    }

#undef ls
#undef rs
} tree;

inline void solvel() {
    int l=1,r=m,res=0;
    while(l<=r) {
        int mid=l+r>>1;
        int x=tree.query(1,1,m,mid);
        if(x<L) {
            res=mid;
            l=mid+1;
        } else r=mid-1;
    }
    tree.change(1,1,m,1,res,0,L);
}

inline void solver() {
    int l=1,r=m,res=m+1;
    while(l<=r) {
        int mid=l+r>>1;
        int x=tree.query(1,1,m,mid);
        if(x>R) {
            res=mid;
            r=mid-1;
        } else l=mid+1;
    }
    tree.change(1,1,m,res,m,0,R);
}

总结一下,这题其实利用了线段树的区间修改的时间复杂度,操作的不改变相对关系。

posted @ 2023-08-03 10:37  djh0314  阅读(28)  评论(0)    收藏  举报  来源
浏览器标题切换
浏览器标题切换end