【SCOI2010】序列操作(求和+连续最长线段树)

临近期末,说什么竞赛辅导全都停了,一切为期末考试让路,我反正已经考不好了,我还是像往常一样来机房,同级的大佬们都在教室学习,我一人不知道刷些什么,就熟练地点开了——

Created with Raphaël 2.1.2 洛谷 打卡 题目 线段树 按难度排序 找到(浅)蓝题 我想做吗? 开始写代码 yes no

没错我只是来玩Markdown的,我就找到了如下题目,是道紫题,标签有且只有线段树。本着挑战的精神,我就做了两天…对于我这种蒟蒻来说,显然是借鉴了题解的部分思路。



题目描述

顾佬最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作:

0 a b 把[a, b]区间内的所有数全变成0

1 a b 把[a, b]区间内的所有数全变成1

2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0

3 a b 询问[a, b]区间内总共有多少个1

4 a b 询问[a, b]区间内最多有多少个连续的1

对于每一种询问操作,顾佬都需要给出回答,聪明的程序员们,你们能帮助他吗?

输入输出格式

输入格式:

输入数据第一行包括2个数,n和m,分别表示序列的长度和操作数目

第二行包括n个数,表示序列的初始状态

接下来m行,每行3个数,op, a, b,(0<=op<=4,0<=a<=b< n)表示对于区间[a, b]执行标号为op的操作

输出格式:

对于每一个询问操作,输出一行,包括1个数,表示其对应的答案



首先这道题的难点就是如何维护和查询区间内最长连续1的个数,这部分许多类似题目都有。


要分情况,向上合并后区间的最长连续1可能等于:
1. 区间的连续最长1;
2. 区间的连续最长1;
3. 左右区间的中间部分;
灵性的地方来了,第3种情况如何实现呢,不难想到可以维护每个区间左起1和右起1的个数,然后三种情况用max括起来。
然后这两个新信息又如何向上合并呢?思考一下,对于左起,情况如下:
1. 若左子树全部为1,根的左起1就是左区间长度加上右字树的左起1
2. 否则就是左子树的左起1
右起同理。


这样一来就要维护三个信息了,再看看修改操作中,需要反转…那么再分别维护这三个信息的0值版本,翻转时进行swap。
此处要注意覆盖操作的优先级高于翻转,也就是说先下放覆盖标签。
嗯,有够麻烦。


但是查询起来也不是那么简单的,因为最终是一个区间内的连续最长1,所以查询函数每次返回一个节点,然后分情况讨论,向上递归。那么依然对于左起:
1. 如果左子树为空,当前节点左起1为其右子树左起1
2. 如果左子树不空:
(1)若左子树左起1等于左子树区间长度,当前节点左起1为左子树区间长加右子树左起1
(2)否则当前节点左起1为其左子树左起1
右起同理,最终区间最长1同向上合并操作。
最后返回查询区间的最终最长连续1即可


求和什么的就不必多说,常规操作就好。


代码如下:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100002;
int a[maxn],n,m,i,x,y,s;
struct node{int l,r,t;}ha;
struct segtree{
    #define lson (o<<1)
    #define rson (o<<1|1)
    #define mid ((l+r)>>1)
    int sumv[maxn<<2],suml[maxn<<2],sumr[maxn<<2],sumt[maxn<<2];//v为value,l为left,r为right,t为total
    int sumlz[maxn<<2],sumrz[maxn<<2],sumtz[maxn<<2];//上一行信息的0值版本,翻转时用
    int reiv[maxn<<2],itiv[maxn<<2],xorv[maxn<<2];//三个标记,这鸟语的0和1就不要在意了
    inline void pushup(int o,int l,int r){//向上合并
        if(suml[lson]==mid-l+1)suml[o]=suml[lson]+suml[rson];//第1种情况
         else suml[o]=suml[lson];//第2种情况
        if(sumr[rson]==r-mid)sumr[o]=sumr[rson]+sumr[lson];
         else sumr[o]=sumr[rson];//右起同理
        sumt[o]=max(max(sumt[rson],sumt[lson]),suml[rson]+sumr[lson]);//实际最长1的三种情况
        if(sumlz[lson]==mid-l+1)sumlz[o]=sumlz[lson]+sumlz[rson];
         else sumlz[o]=sumlz[lson];
        if(sumrz[rson]==r-mid)sumrz[o]=sumrz[rson]+sumrz[lson];
         else sumrz[o]=sumrz[rson];
        sumtz[o]=max(max(sumtz[rson],sumtz[lson]),sumlz[rson]+sumrz[lson]);//0值版本同理
        sumv[o]=sumv[lson]+sumv[rson];
    }
    inline void pushdown(int o,int l,int r){//下放
        if(itiv[o]){
            sumv[lson]=suml[lson]=sumr[lson]=sumt[lson]=mid-l+1;
            sumv[rson]=suml[rson]=sumr[rson]=sumt[rson]=r-mid;
            sumlz[lson]=sumlz[rson]=sumrz[lson]=sumrz[rson]=sumtz[lson]=sumtz[rson]=0;
            itiv[rson]=itiv[lson]=1;
            xorv[lson]=xorv[rson]=reiv[lson]=reiv[rson]=0;
            itiv[o]=0;
        }else if(reiv[o]){
            sumlz[lson]=sumrz[lson]=sumtz[lson]=mid-l+1;
            sumlz[rson]=sumrz[rson]=sumtz[rson]=r-mid;
            sumv[lson]=sumv[rson]=suml[lson]=suml[rson]=sumr[lson]=sumr[rson]=sumt[lson]=sumt[rson]=0;
            reiv[rson]=reiv[lson]=1;
            xorv[lson]=xorv[rson]=itiv[lson]=itiv[rson]=0;
            reiv[o]=0;
        }
        //注意并列的标签不要加else,一并下推
        if(xorv[o]){
            sumv[lson]=mid-l+1-sumv[lson],sumv[rson]=r-mid-sumv[rson];
            swap(sumt[lson],sumtz[lson]);swap(sumt[rson],sumtz[rson]);
            swap(suml[lson],sumlz[lson]);swap(suml[rson],sumlz[rson]);
            swap(sumr[lson],sumrz[lson]);swap(sumr[rson],sumrz[rson]);
            xorv[lson]^=1;xorv[rson]^=1;
            xorv[o]=0;
        }
    }
    inline void build(int o,int l,int r){建树
        reiv[o]=itiv[o]=xorv[o]=0;
        if(l==r){
         suml[o]=sumr[o]=sumt[o]=sumv[o]=a[l];
         sumlz[o]=sumrz[o]=sumtz[o]=a[l]^1;
         return;
        }
        build(lson,l,mid);
        build(rson,mid+1,r);
        pushup(o,l,r);
    }
    inline int querysum(int o,int l,int r,int ql,int qr){//求和,即1的个数
        if(ql<=l&&qr>=r)return sumv[o];
        pushdown(o,l,r);
        int ans=0;
        if(ql<=mid)ans+=querysum(lson,l,mid,ql,qr);
        if(qr>mid)ans+=querysum(rson,mid+1,r,ql,qr);
        return ans;
    }
    inline node querymax(int o,int l,int r,int ql,int qr){//求最长连续1
        if(ql<=l&&qr>=r)return (node){suml[o],sumr[o],sumt[o]};
        pushdown(o,l,r);
        node t,tl,tr;//用结构体分别维护三个信息
        int fll=0,flr=0;//判断子树是否为空的flag
        if(ql<=mid)tl=querymax(lson,l,mid,ql,qr);else{tl=(node){0,0,0};fll=1;}//赋成0,以免影响最终区间内最长连续1的比较
        if(qr>mid)tr=querymax(rson,mid+1,r,ql,qr);else{tr=(node){0,0,0};flr=1;}
        if(tl.l==mid-l+1)t.l=tl.l+tr.l;//1
         else if(fll==1)t.l=tr.l;//2(1)
          else t.l=tl.l;//2(2)
        if(tr.r==r-mid)t.r=tr.r+tl.r;
         else if(flr==1)t.r=tl.r;
          else t.r=tr.r;
        t.t=max(max(tr.t,tl.t),tl.r+tr.l);
        return t;
    }
    inline void optiti(int o,int l,int r,int ql,int qr){//区间赋为1
        if(ql<=l&&qr>=r){
            sumv[o]=r-l+1;
            sumlz[o]=sumrz[o]=sumtz[o]=0;
            suml[o]=sumr[o]=sumt[o]=r-l+1;
            xorv[o]=0,itiv[o]=1,reiv[o]=0;
            return;
        }
        pushdown(o,l,r);
        if(ql<=mid)optiti(lson,l,mid,ql,qr);
        if(qr>mid)optiti(rson,mid+1,r,ql,qr);
        pushup(o,l,r);
    }
    inline void optrei(int o,int l,int r,int ql,int qr){//区间赋为0
        if(ql<=l&&qr>=r){
            sumv[o]=0;
            sumlz[o]=sumrz[o]=sumtz[o]=r-l+1;
            suml[o]=sumr[o]=sumt[o]=0;
            xorv[o]=0,itiv[o]=0,reiv[o]=1;
            return;
        }
        pushdown(o,l,r);
        if(ql<=mid)optrei(lson,l,mid,ql,qr);
        if(qr>mid)optrei(rson,mid+1,r,ql,qr);
        pushup(o,l,r);
    }
    inline void optxor(int o,int l,int r,int ql,int qr){//区间翻转
        if(ql<=l&&qr>=r){
            sumv[o]=r-l+1-sumv[o];
            swap(sumt[o],sumtz[o]);
            swap(suml[o],sumlz[o]);
            swap(sumr[o],sumrz[o]);
            xorv[o]^=1;
            return;
        }
        pushdown(o,l,r);
        if(ql<=mid)optxor(lson,l,mid,ql,qr);
        if(qr>mid)optxor(rson,mid+1,r,ql,qr);
        pushup(o,l,r);
    }
}seg;
int main(){
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    seg.build(1,1,n);
    while(m--){
        scanf("%d%d%d",&s,&x,&y);
        x++;y++;
        switch(s){//最好加上break
            case 0:seg.optrei(1,1,n,x,y);break;
            case 1:seg.optiti(1,1,n,x,y);break;
            case 2:seg.optxor(1,1,n,x,y);break;
            case 3:printf("%d\n",seg.querysum(1,1,n,x,y));break;
            case 4:printf("%d\n",seg.querymax(1,1,n,x,y).t);break;
        }
    }
    return 0;
}

顺便一说,这是我A掉的第一道紫题,恐怖如斯啊。
这一个学期马上要结束了,上半年初中时代的我最多只敢做黄题,上了高中简直开眼界。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值