HDU3397(Sequence operation)线段树的多种操作

/**********************************************
题目大意:
0 a b将区间[a,b]所有数全部变成0
1 a b将区间[a,b]所有数全部变成1
2 a b将区间[a,b]中所有数0 1互换,0变1,1变0
3 a b输出区间[a,b]中1的个数
4 a b输出区间[a,b]中最长连续1的个数

算法分析:
涉及到线段树的多种操作;
0,1两种操作可以合并到一起写;
0,1互换即线段树的异或操作;
查询区间最长连续1个数的过程中;
maxl=[l,m]上最长连续1个数;
maxl=[m+1,r]上最长连续1的个数;
maxm=min(m-l+1,左孩子的rs)+min(r-m,右孩子的ls);
结果应该是这三个中的最大值,即max(maxl,maxr,maxm);
其中查询操作和pku3667类似;
***********************************************/
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<climits>
#include<algorithm>
using namespace std;

#define L l,m,u<<1
#define R m+1,r,u<<1|1

const int N =100010;

struct node
{
    int l,r;
    int ls,ms,rs;//ms存储最长连续1的数量
    int flag;//01状态
    int total;//区间1的总数
} t[N*3];

int len(int u)
{
    return t[u].r-t[u].l+1;
}

int mid(int u)
{
    return (t[u].l+t[u].r)>>1;
}

void sets(int u,int x)
{
    t[u].ls=t[u].ms=t[u].rs=t[u].total=x*(t[u].r-t[u].l+1);
    t[u].flag=x;
}

void build(int l,int r,int u)
{
    t[u].l=l;
    t[u].r=r;
    t[u].ls=t[u].rs=t[u].ms=t[u].total=t[u].flag=0;
    if(l==r)
        return;
    int m=mid(u);
    build(L);
    build(R);

}

void PushUp(int u)//把当前结点的信息更新到父结点
{
    t[u].flag=(t[u<<1].flag==t[u<<1|1].flag?t[u<<1].flag:-1);//更新状态
    t[u].ls=t[u<<1].ls+((t[u<<1].ls==len(u<<1))?t[u<<1|1].ls:0);//更新左
    t[u].rs=t[u<<1|1].rs+((t[u<<1|1].rs==len(u<<1|1))?t[u<<1].rs:0);//更新右
    t[u].ms=max(t[u<<1].rs+t[u<<1|1].ls,max(t[u<<1].ms,t[u<<1|1].ms));//更新整体
    t[u].total=t[u<<1].total+t[u<<1|1].total;//更新1的总数
}

void update(int l,int r,int u,int x)//更新区间[l,r]0、1状态
{
    if(t[u].flag==x)
        return;
    if(t[u].l==l&&t[u].r==r)
    {
        sets(u,x);
        return;
    }
    if(t[u].flag>=0)
    {
        sets(u<<1,t[u].flag);
        sets(u<<1|1,t[u].flag);
        t[u].flag=-1;
    }
    int m=mid(u);
    if(r<=m)
    {
        update(l,r,u<<1,x);
    }
    else if(l>m)
    {
        update(l,r,u<<1|1,x);
    }
    else
    {
        update(L,x);
        update(R,x);
    }
    PushUp(u);
}

void swap(int l,int r,int u)//[l,r]0、1交换
{
    if(t[u].l>=l&&t[u].r<=r&&(t[u].flag==0||t[u].flag==1))
    {
        int x=t[u].flag^1;
        sets(u,x);
        return;
    }
    if(t[u].flag>=0)
    {
        sets(u<<1,t[u].flag);
        sets(u<<1|1,t[u].flag);
    }
    int m=mid(u);
    if(r<=m)
    {
        swap(l,r,u<<1);
    }
    else if(l>m)
    {
        swap(l,r,u<<1|1);
    }
    else
    {
        swap(L);
        swap(R);
    }
    PushUp(u);
}

int find1(int l,int r,int u)//找区间[l,r]内的1的个数
{
    if(t[u].l==l&&t[u].r==r)
    {
        return t[u].total;
    }
    if(t[u].flag>=0)
    {
        sets(u<<1,t[u].flag);
        sets(u<<1|1,t[u].flag);
    }
    int m=mid(u);
    if(r<=m)
    {
        return find1(l,r,u<<1);
    }
    else if(l>m)
    {
        return find1(l,r,u<<1|1);
    }
    else
    {
        return find1(L)+find1(R);
    }
}

int find2(int l,int r,int u)//找区间[l,r]内最长连续1的个数
{
    if(t[u].l==l&&t[u].r==r)
    {
        return t[u].ms;
    }
    if(t[u].flag>=0)
    {
        sets(u<<1,t[u].flag);
        sets(u<<1|1,t[u].flag);
    }
    int m=mid(u);
    if(r<=m)
    {
        return find2(l,r,u<<1);
    }
    else if(l>m)
    {
        return find2(l,r,u<<1|1);
    }
    else
    {
        int maxl=find2(L);
        int maxr=find2(R);
        int maxm=min(m-l+1,t[u<<1].rs)+min(r-m,t[u<<1|1].ls);
        return max(maxm,max(maxl,maxr));
    }
}

int main()
{
    //freopen("C:\\Users\\Administrator\\Desktop\\kd.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        build(0,n-1,1);
        for(int i=0; i<n; i++)
        {
            int x;
            scanf("%d",&x);
            if(x!=0)
                update(i,i,1,x);
        }
        for(int i=0; i<m; i++)
        {
            int op,a,b;
            scanf("%d%d%d",&op,&a,&b);
            if(op==0||op==1)
                update(a,b,1,op);
            else if(op==2)
                swap(a,b,1);
            else if(op==3)
                printf("%d\n",find1(a,b,1));
            else if(op==4)
                printf("%d\n",find2(a,b,1));
        }
    }
    return 0;
}

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值