小A的题(线段树)

本文介绍了一道关于01序列排序的题目,小A需要通过局部升序或降序排序操作,使01串变为有趣的序列。提供了操作类型、输入输出格式以及数据范围,并给出样例输入和输出。

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

描述

由于小 A 实在是太菜了,因此他现在需要你的帮助: 现在小 A 手上有一个凌乱的 01 串,他想通过若干次对于这个 01 串的局部排序将它变成一个有趣的 01 序列。 现在有两种操作:

输入格式

  1. l r 0 表示把区间 [l,r][l,r] 给升序排序

  2. l rr1 表示把区间 [l,r][l,r] 给降序排序

然后小 A 这个菜鸡想知道在 m 次操作之后序列长啥样。

输出格式

第一行一个 0101 串 S。 第二行一个整数 m。 接下来 m 行每行三个整数 l,r,x,   x=0,1中的一个。

m 次操作之后的 01 串

数据范围

∣S∣≤1000000,m≤500000

输出时每行末尾的多余空格,不影响答案正确性

样例输入

11001
1
2 4 0

样例输出

10011

 

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=1000100;
int a[maxn];
int sum[maxn*4];
int Add[maxn*4];
char str[maxn];
int n,m,x,y,z,q,len;
void PushUp(int n)
{
    sum[n]=sum[n*2]+sum[n*2+1];
    return;
}

void Build(int l,int r,int num)
{
    if(l==r)
    {
        sum[num]=a[l];
        return;
    }
    int m=(l+r)/2;
    Build(l,m,num*2);
    Build(m+1,r,num*2+1);
    PushUp(num);
    return;
}

void PushDown(int num,int ln,int rn)
{
    if(Add[num]==1)
    {
        Add[num*2]=1;
        Add[num*2+1]=1;
        sum[num*2]=ln;
        sum[num*2+1]=rn;
        Add[num]=0;
    }
    else if(Add[num]==-1)
    {
        Add[num*2]=-1;
        Add[num*2+1]=-1;
        sum[num*2]=0;
        sum[num*2+1]=0;
        Add[num]=0;
    }
    return;
}

void Update(int L,int R,int C,int l,int r,int num)
{
    if(l>=L&&R>=r)
    {
        if(C==0)
        {
            if(Add[num]!=0)
            {
                int m=(l+r)/2;
                PushDown(num,m-l+1,r-m);
            }
            sum[num]=0;
            Add[num]=-1;
            return;
        }
        if(Add[num]!=0)
        {
            int m=(l+r)/2;
            PushDown(num,m-l+1,r-m);
        }
        sum[num]=r-l+1;
        Add[num]=1;
        return;
    }
    int m=(l+r)/2;
    PushDown(num,m-l+1,r-m);
    if(L<=m)
        Update(L,R,C,l,m,num*2);
    if(R>=m+1)
        Update(L,R,C,m+1,r,num*2+1);
    PushUp(num);
    return;
}

int Query(int L,int R,int l,int r,int num)
{
    if(L<=l&&R>=r)
    {
        return sum[num];
    }
    int m=(l+r)/2;
    PushDown(num,m-l+1,r-m);
    int ans=0;
    if(L<=m)
        ans+=Query(L,R,l,m,num*2);
    if(R>=m+1)
        ans+=Query(L,R,m+1,r,num*2+1);
    return ans;
}

void print(int l,int r,int num)
{
    if(l==r)
    {
        if(sum[num]==1)
            printf("1");
        else
            printf("0");
        return;
    }
    if(sum[num]==r-l+1)
    {
        for(int i=l;i<=r;i++)
            printf("1");
        return;
    }
    if(sum[num]==0)
    {
        for(int i=l;i<=r;i++)
            printf("0");
        return;
    }
    int m=(l+r)/2;
    PushDown(num,m-l+1,r-m);
    print(l,m,num*2);
    print(m+1,r,num*2+1);
}

int main()
{
    memset(Add,0,sizeof(Add));
    scanf("%s",str+1);
    len=strlen(str+1);
    for(int i=1; i<=len; i++)
    {
        if(str[i]=='1')
            a[i]=1;
        else
            a[i]=0;
    }
    Build(1,len,1);
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d %d %d",&x,&y,&z);
        int s=Query(x,y,1,len,1);
        if(s==0)
            continue;
        if(s==y-x+1)
            continue;
        Update(x,y,0,1,len,1);
        if(z==0)
            Update(y-s+1,y,1,1,len,1);
        else
            Update(x,x+s-1,1,1,len,1);
    }
    print(1,len,1);
    return 0;
}

 

### 关于线段树算法的练习 以下是几道经典的线段树算法练习,涵盖了不同难度和应用场景: #### 1. **区间修改与查询** 这是一道基础的线段树目,涉及单点修改、区间查询以及懒惰标记的应用。 输入一个长度为 `n` 的数组,支持两种操作: - 修改指定区间的每个元素加上某个值。 - 查询某一段区间的总和。 参考实现如下: ```cpp struct Node { long long sum; int lazy; }; void pushUp(int rt) { tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum; } void buildTree(int rt, int l, int r) { if (l == r) { tree[rt].sum = a[l]; return; } int m = (l + r) >> 1; buildTree(rt << 1, l, m); buildTree(rt << 1 | 1, m + 1, r); pushUp(rt); } void updateRange(int rt, int l, int r, int ul, int ur, int val) { if (ul <= l && r <= ur) { tree[rt].sum += (r - l + 1) * val; tree[rt].lazy += val; return; } int m = (l + r) >> 1; if (tree[rt].lazy != 0) { tree[rt << 1].sum += (m - l + 1) * tree[rt].lazy; tree[rt << 1 | 1].sum += (r - m) * tree[rt].lazy; tree[rt << 1].lazy += tree[rt].lazy; tree[rt << 1 | 1].lazy += tree[rt].lazy; tree[rt].lazy = 0; } if (ul <= m) updateRange(rt << 1, l, m, ul, ur, val); if (ur > m) updateRange(rt << 1 | 1, m + 1, r, ul, ur, val); pushUp(rt); } long long querySum(int rt, int l, int r, int ql, int qr) { if (ql <= l && r <= qr) return tree[rt].sum; int m = (l + r) >> 1; if (tree[rt].lazy != 0) { tree[rt << 1].sum += (m - l + 1) * tree[rt].lazy; tree[rt << 1 | 1].sum += (r - m) * tree[rt].lazy; tree[rt << 1].lazy += tree[rt].lazy; tree[rt << 1 | 1].lazy += tree[rt].lazy; tree[rt].lazy = 0; } long long res = 0; if (ql <= m) res += querySum(rt << 1, l, m, ql, qr); if (qr > m) res += querySum(rt << 1 | 1, m + 1, r, ql, qr); return res; } ``` --- #### 2. **窗口中的星星** 此来源于扫描线算法的经典应用之一——计算矩形覆盖区域内的星星数量。通过构建一棵维护横坐标范围信息的线段树,动态更新每一层被覆盖的情况并统计总数[^1]。 --- #### 3. **订单管理问** 描述:给定若干天数和多个订单请求,判断是否存在冲突情况。如果存在,则返回需要调整的具体订单编号。此可以通过离散化处理时间轴上的事件,并利用线段树高效完成区间最大值或最小值的查找任务[^4]。 --- #### 4. **序列变换求和** 目背景设定较为复杂,但核心仍是基于线段树的操作逻辑。具体而言,在面对连续增加平方项至目标范围内各位置的任务时,需巧妙设计状态转移方程以减少冗余运算量[^5]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值