[BZOJ 1858][Scoi2010]序列操作(线段树)

本文介绍了一种使用线段树解决01序列变换及查询问题的方法。通过对线段树进行懒惰传播和反转操作,可以高效地完成序列的修改与统计任务。文章详细解释了代码实现细节,并提供了完整的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description


lxhgww最近收到了一个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 对于每一种询问操作,lxhgww都需要给出回答,聪明的程序员们,你们能帮助他吗?

Input


输入数据第一行包括2个数,n和m,分别表示序列的长度和操作数目 第二行包括n个数,表示序列的初始状态 接下来m行,每行3个数,op, a, b,(0<=op<=4,0<=a<=b

Output


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

Sample Input


10 10
0 0 0 1 1 0 1 0 1 1
1 0 2
3 0 5
2 2 2
4 0 4
0 3 6
2 3 7
4 2 8
1 0 5
0 5 6
3 3 9

Sample Output


5
2
6
5

HINT


对于30%的数据,1<=n, m<=1000
对于100%的数据,1<=n, m<=100000

Solution


啊为什么调了那么长时间…
后来发现是push_down操作给子节点标lazy的时候忘记把它们的rev标记去掉了 泪

线段记录区间里1和0的个数、最长连续的1和0的个数、左端连续的1和0的个数、右端连续的1和0的个数,然后记录一个lazy标记和rev标记(取反)。
取反操作的时候遇到lazy标记可以直接对lazy取反,标记lazy的时候要记得把rev去掉
那个最长连续1的询问函数直接返回了一个结构体,合并操作和线段树的更新差不多,具体实现看代码

感觉我没有办法写出优美的代码,不知道为什么代码长度这么长= =

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<assert.h>
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define mid(a,b) ((a+b)>>1)
#define MAXN 100005
using namespace std;
int n,m;
int x[MAXN],ans;
struct Node{
    int l,r,siz;
    int num1,lnum1,rnum1,maxnum1;
    int num0,lnum0,rnum0,maxnum0;
    int lazy,rev;
}t[MAXN*4];
Node operator + (const Node& a,const Node& b)
{
    Node ans;

    ans.lnum0=a.lnum0;
    if(a.lnum0==a.siz)
    ans.lnum0=a.siz+b.lnum0;
    ans.lnum1=a.lnum1;
    if(a.lnum1==a.siz)
    ans.lnum1=a.siz+b.lnum1;

    ans.rnum0=b.rnum0;
    if(b.rnum0==b.siz)
    ans.rnum0=b.siz+a.rnum0;
    ans.rnum1=b.rnum1;
    if(b.rnum1==b.siz)
    ans.rnum1=b.siz+a.rnum1;

    ans.num0=a.num0+b.num0;
    ans.num1=a.num1+b.num1;

    ans.maxnum0=Max(a.maxnum0,b.maxnum0);
    ans.maxnum0=Max(ans.maxnum0,a.rnum0+b.lnum0);

    ans.maxnum1=Max(a.maxnum1,b.maxnum1);
    ans.maxnum1=Max(ans.maxnum1,a.rnum1+b.lnum1);
    return ans;
}
void _exchange(int idx)
{
    swap(t[idx].lnum0,t[idx].lnum1);
    swap(t[idx].rnum0,t[idx].rnum1);
    swap(t[idx].num0,t[idx].num1);
    swap(t[idx].maxnum0,t[idx].maxnum1);
}
void push_down(int idx)
{
    if(t[idx].l==t[idx].r)return;
    if(t[idx].lazy!=-1)
    {
        int f=t[idx].lazy;
        t[idx*2].lazy=t[idx*2+1].lazy=t[idx].lazy;
        t[idx*2].rev=t[idx*2+1].rev=0;
        t[idx].lazy=-1;
        t[idx*2].lnum0=t[idx*2].rnum0=t[idx*2].num0=t[idx*2].maxnum0=(f==0)*t[idx*2].siz;
        t[idx*2].lnum1=t[idx*2].rnum1=t[idx*2].num1=t[idx*2].maxnum1=(f==1)*t[idx*2].siz;
        t[idx*2+1].lnum0=t[idx*2+1].rnum0=t[idx*2+1].num0=t[idx*2+1].maxnum0=(f==0)*t[idx*2+1].siz;
        t[idx*2+1].lnum1=t[idx*2+1].rnum1=t[idx*2+1].num1=t[idx*2+1].maxnum1=(f==1)*t[idx*2+1].siz;
    }
    if(t[idx].rev)
    {
        t[idx].rev=0;
        t[idx*2].rev^=1;
        t[idx*2+1].rev^=1;
        _exchange(idx*2);
        _exchange(idx*2+1);
    }
}
void _update(int idx)
{
    t[idx].lnum0=t[idx*2].lnum0;
    if(t[idx*2].lnum0==t[idx*2].siz)
    t[idx].lnum0=t[idx*2].siz+t[idx*2+1].lnum0;
    t[idx].lnum1=t[idx*2].lnum1;
    if(t[idx*2].lnum1==t[idx*2].siz)
    t[idx].lnum1=t[idx*2].siz+t[idx*2+1].lnum1;

    t[idx].rnum0=t[idx*2+1].rnum0;
    if(t[idx*2+1].rnum0==t[idx*2+1].siz)
    t[idx].rnum0=t[idx*2+1].siz+t[idx*2].rnum0;
    t[idx].rnum1=t[idx*2+1].rnum1;
    if(t[idx*2+1].rnum1==t[idx*2+1].siz)
    t[idx].rnum1=t[idx*2+1].siz+t[idx*2].rnum1;

    t[idx].num0=t[idx*2].num0+t[idx*2+1].num0;
    t[idx].num1=t[idx*2].num1+t[idx*2+1].num1;

    t[idx].maxnum0=Max(t[idx*2].maxnum0,t[idx*2+1].maxnum0);
    t[idx].maxnum0=Max(t[idx].maxnum0,t[idx*2].rnum0+t[idx*2+1].lnum0);

    t[idx].maxnum1=Max(t[idx*2].maxnum1,t[idx*2+1].maxnum1);
    t[idx].maxnum1=Max(t[idx].maxnum1,t[idx*2].rnum1+t[idx*2+1].lnum1);
}
void _build(int idx,int a,int b)
{
    t[idx].siz=b-a+1;
    t[idx].l=a;t[idx].r=b;
    t[idx].lazy=-1;
    t[idx].rev=0;
    if(a==b)
    {
        if(x[a])
        {
            t[idx].lnum0=t[idx].rnum0=t[idx].num0=t[idx].maxnum0=0;
            t[idx].lnum1=t[idx].rnum1=t[idx].num1=t[idx].maxnum1=1;
        }
        else 
        {
            t[idx].lnum0=t[idx].rnum0=t[idx].num0=t[idx].maxnum0=1;
            t[idx].lnum1=t[idx].rnum1=t[idx].num1=t[idx].maxnum1=0;
        }
        return;
    }
    int m=mid(a,b);
    _build(idx*2,a,m);
    _build(idx*2+1,m+1,b);
    _update(idx);
}
void _turn(int idx,int a,int b,int f)
{
    push_down(idx);
    if(t[idx].l==t[idx].r)
    {
        t[idx].lnum0=t[idx].rnum0=t[idx].num0=t[idx].maxnum0=(f==0);
        t[idx].lnum1=t[idx].rnum1=t[idx].num1=t[idx].maxnum1=(f==1);
        return;
    }
    if(a<=t[idx].l&&b>=t[idx].r)
    {
        t[idx].lnum0=t[idx].rnum0=t[idx].num0=t[idx].maxnum0=(f==0)*t[idx].siz;
        t[idx].lnum1=t[idx].rnum1=t[idx].num1=t[idx].maxnum1=(f==1)*t[idx].siz;
        if(t[idx].rev)
        t[idx].rev=0;
        t[idx].lazy=f;
        return;
    }

    int m=mid(t[idx].l,t[idx].r);
    if(b<=m)_turn(idx*2,a,b,f);
    else if(a>m)_turn(idx*2+1,a,b,f);
    else
    {
        _turn(idx*2,a,b,f);
        _turn(idx*2+1,a,b,f);
    }
    _update(idx);
}
void _reverse(int idx,int a,int b)
{
    push_down(idx);
    if(t[idx].l==t[idx].r)
    {
        _exchange(idx);
        return;
    }
    if(a<=t[idx].l&&b>=t[idx].r)
    {
        if(t[idx].lazy!=-1)
        {
            t[idx].lazy^=1;
            _exchange(idx);
            return;
        }
        t[idx].rev^=1;
        _exchange(idx);
        return;
    }
    int m=mid(t[idx].l,t[idx].r);
    if(b<=m)_reverse(idx*2,a,b);
    else if(a>m)_reverse(idx*2+1,a,b);
    else
    {
        _reverse(idx*2,a,b);
        _reverse(idx*2+1,a,b);
    }
    _update(idx);
}
void _count(int idx,int a,int b)
{
    push_down(idx);
    if(t[idx].l==t[idx].r)
    {
        ans+=t[idx].num1;
        return;
    }

    if(a<=t[idx].l&&b>=t[idx].r)
    {
        ans+=t[idx].num1;
        return;
    }

    int m=mid(t[idx].l,t[idx].r);
    if(b<=m)_count(idx*2,a,b);
    else if(a>m)_count(idx*2+1,a,b);
    else
    {
        _count(idx*2,a,b);
        _count(idx*2+1,a,b);
    }
}
Node _query(int idx,int a,int b)
{
    push_down(idx);
    if(a==t[idx].l&&b==t[idx].r)
    {
        return t[idx];
    }

    int m=mid(t[idx].l,t[idx].r);
    if(a<=m&&b>m)
    {
        return _query(idx*2,a,m)+_query(idx*2+1,m+1,b);
    }
    if(a>m)
    {
        return _query(idx*2+1,a,b);
    }
    if(b<=m)
    {
        return _query(idx*2,a,b);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
    scanf("%d",&x[i]);
    _build(1,0,n-1);
    for(int i=1;i<=m;i++)
    {
        int opt,a,b;
        scanf("%d%d%d",&opt,&a,&b);
        switch(opt)
        {
            case 0:
            _turn(1,a,b,0);break;
            case 1:
            _turn(1,a,b,1);break;   
            case 2:
            _reverse(1,a,b);break;
            case 3:
            ans=0;_count(1,a,b);printf("%d\n",ans);break;
            case 4:
            Node res=_query(1,a,b);
            printf("%d\n",res.maxnum1);break;

        }
    }
    return 0;
}
一、综合实战—使用极轴追踪方式绘制信号灯 实战目标:利用对象捕捉追踪和极轴追踪功能创建信号灯图形 技术要点:结合两种追踪方式实现精确绘图,适用于工程制图中需要精确定位的场景 1. 切换至AutoCAD 操作步骤: 启动AutoCAD 2016软件 打开随书光盘中的素材文件 确认工作空间为"草图与注释"模式 2. 绘图设置 1)草图设置对话框 打开方式:通过"工具→绘图设置"菜单命令 功能定位:该对话框包含捕捉、追踪等核心绘图辅助功能设置 2)对象捕捉设置 关键配置: 启用对象捕捉(F3快捷键) 启用对象捕捉追踪(F11快捷键) 勾选端点、中心、圆心、象限点等常用捕捉模式 追踪原理:命令执行时悬停光标可显示追踪矢量,再次悬停可停止追踪 3)极轴追踪设置 参数设置: 启用极轴追踪功能 设置角度增量为45度 确认后退出对话框 3. 绘制信号灯 1)绘制圆形 执行命令:"绘图→圆→圆心、半径"命令 绘制过程: 使用对象捕捉追踪定位矩形中心作为圆心 输入半径值30并按Enter确认 通过象限点捕捉确保圆形位置准确 2)绘制直线 操作要点: 选择"绘图→直线"命令 捕捉矩形上边中点作为起点 捕捉圆的上象限点作为终点 按Enter结束当前直线命令 重复技巧: 按Enter可重复最近使用的直线命令 通过圆心捕捉和极轴追踪绘制放射状直线 最终形成完整的信号灯指示图案 3)完成绘制 验证要点: 检查所有直线是否准确连接圆心和象限点 确认极轴追踪的45度增量是否体现 保存绘图文件(快捷键Ctrl+S)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值