HDU 3397 Sequence operation(线段…

本文介绍了一种使用线段树进行复杂操作优化的方法,包括区间修改、区间查询及区间最长连续序列查找,适用于处理大量数据的高效算法场景。

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

题意:给个一只有0,1 的序列,有以下5种操作
0 a b change all characters into '0's in [a , b] //把a,b间的都改为0
1 a b change all characters into '1's in [a , b] //把a,b间都改为1
2 a b change all '0's into '1's and change all '1's into '0's in [a, b] //1的改为0,0的改为1
Output operations:
3 a b output the number of '1's in [a, b]  //a,b间1 的数量
4 a b output the length of the longest continuous '1' string in [a , b]//a,b间最大连续的1的个数

思路:经典的线段树,只是操作过多,但其实很简单,相当于把以前做过的题的操作合在一块 什么值的更改,区间和的查询,最开连续的个数 。所以没什么特殊的

//1312MS    8836K
#include <stdio.h>
#define L(x) (x<<1)
#define R(x) (x<<1|1)
#define M 100010

struct tree
{
    int l,r,len;  //len 是该区间的长度
    int sum,cover;   //sum 是该区间1的数量,cover 有3个值-1,0,1分别表示该区间是否被0,1完全覆盖
    int lm,rm,ma;    //lm rm ma 分别表示 左连续 右连续 和最大的连续1的个数
} node[M*4];

int num[M];

int max (int a,int b)
{
    return a > b ? a : b;
}
int min (int a ,int b)
{
    return a > b ? b : a;
}
void Union (int u)  //区间合并,由子结点修改父结点的信息
                    //常用手法 就不多解释了
    node[u].sum = node[L(u)].sum + node[R(u)].sum;
    if (node[L(u)].len == node[L(u)].lm)
        node[u].lm = node[L(u)].len + node[R(u)].lm;
    else
        node[u].lm = node[L(u)].lm;
    if (node[R(u)].len == node[R(u)].rm)
        node[u].rm = node[R(u)].len + node[L(u)].rm;
    else
        node[u].rm = node[R(u)].rm;
    int a = max (node[L(u)].ma,node[R(u)].ma);
    int b = max (node[u].lm,node[u].rm);
    node[u].ma = max (max (a,b),node[L(u)].rm + node[R(u)].lm);
}
void Build (int u,int left,int right)
{
    node[u].l = left;
    node[u].r = right;
    node[u].len = right - left + 1;
    node[u].cover = -1;
    if (left == right)
    {
        if (num[left])
        {
            node[u].sum = 1;
            node[u].cover = 1;
            node[u].lm = node[u].rm = node[u].ma = 1;
        }
        else
        {
            node[u].sum = 0;
            node[u].cover = 0;
            node[u].lm = node[u].rm = node[u].ma = 0;
        }
        return ;
    }
    int mid = (left + right)>>1;
    Build (L(u),left,mid);
    Build (R(u),mid+1,right);
    Union (u);
}
void getdown (int u)     //延迟操作,由父结点修改子结点
                       //也是常用手法
    node[L(u)].cover = node[u].cover;
    node[R(u)].cover = node[u].cover;
    if (node[u].cover)
    {
        node[L(u)].lm = node[L(u)].rm = node[L(u)].ma = node[L(u)].len;
        node[L(u)].sum = node[L(u)].len;
        node[R(u)].lm = node[R(u)].rm = node[R(u)].ma = node[R(u)].len;
        node[R(u)].sum = node[R(u)].len;
    }
    else
    {

        node[L(u)].lm = node[L(u)].rm = node[L(u)].ma = 0;
        node[L(u)].sum = 0;
        node[R(u)].lm = node[R(u)].rm = node[R(u)].ma = 0;
        node[R(u)].sum = 0;
    }
    node[u].cover = -1;
}
void Update1 (int u,int left,int right,int flag) //下面这两个Update1,Update2 也算是经典了
                                               //都是一个套路 左边,右边,或一起更新
    if (node[u].l == left &&node[u].r == right)
    {
        node[u].cover = flag;
        if (flag)
        {
            node[u].lm = node[u].rm = node[u].ma = node[u].r - node[u].l + 1;
            node[u].sum = node[u].ma;
        }
        else
        {
            node[u].lm = node[u].rm = node[u].ma = 0;
            node[u].sum = 0;
        }
        return ;
    }
    if (node[u].cover != -1)
        getdown (u);
    int mid = (node[u].l + node[u].r)>>1;
    if (right <= mid)
        Update1 (L(u),left,right,flag);
    else if (left >= mid + 1)
        Update1(R(u),left,right,flag);
    else
    {
        Update1(L(u),left,mid,flag);
        Update1(R(u),mid+1,right,flag);
    }
    Union (u);
}
void Update2 (int u,int left,int right)
{
    if (node[u].l == left&&node[u].r == right&&(node[u].sum == 0||node[u].sum == node[u].len))
    {
        if (node[u].sum > 0)
        {
            node[u].lm = node[u].rm = node[u].ma = 0;
            node[u].sum = 0;
            node[u].cover = 0;
        }
        else
        {
            node[u].lm = node[u].rm = node[u].ma = node[u].len;
            node[u].sum = node[u].len;
            node[u].cover = 1;
        }
        return ;
    }
    if (node[u].cover != -1)
        getdown (u);
    int mid = (node[u].l + node[u].r)>>1;
    if (right <= mid)
        Update2(L(u),left,right);
    else if (left >= mid + 1)
        Update2(R(u),left,right);
    else
    {
        Update2(L(u),left,mid);
        Update2(R(u),mid + 1,right);
    }
    Union (u);
}

int Query (int u,int left,int right,int flag) //查询操作 因为根据flag不同返回不同的值
{
    if (node[u].l == left &&node[u].r == right)
    {
        if (flag)
            return node[u].ma;
        else
            return node[u].sum;
    }
    if (node[u].cover != -1)
        getdown (u);
    int mid = (node[u].l + node[u].r)>>1;
    if (right <= mid)
        return Query(L(u),left,right,flag);
    else if (left >= mid + 1)
        return Query(R(u),left,right,flag);
    else       //这是的return得注意,当返回 连续1的个数时 注意中间断开的情况
            //min (node[L(u)].rm,node[L(u)].r-left+1) 这是求左子结点右边最大的连续 右子结点同理
        if (flag)
        {
            int a = max (Query(L(u),left,mid,flag),Query(R(u),mid+1,right,flag));
            return max (a,min (node[L(u)].rm,node[L(u)].r-left+1) + min (node[R(u)].lm,right-node[R(u)].l + 1));
        }
        else
            return (Query(L(u),left,mid,flag) + Query(R(u),mid+1,right,flag));
    }
}
int main ()
{
    int T,n,m,op,a,b;
    scanf ("%d",&T);
    while (T --)
    {
        scanf ("%d%d",&n,&m);
        for (int i = 1; i <= n; i ++)
            scanf ("%d",&num[i]);
        Build (1,1,n);
        while (m --)
        {
            scanf ("%d%d%d",&op,&a,&b);
            a ++,b++;
            if (op == 0)
                Update1(1,a,b,0);
            else if (op == 1)
                Update1 (1,a,b,1);
            else if (op == 2)
                Update2 (1,a,b);
            else if (op == 3)
                printf ("%d\n",Query (1,a,b,0)); //0 return sum;
            else
                printf ("%d\n",Query (1,a,b,1)); //1 return ma
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值