HDU 3911 Black And White(线段树区间合并+lazy操作)

开始以为是水题,结果。。。。。。
给你一些只有两种颜色的石头,0为白色,1为黑色。
然后两个操作:
1 l r 将[ l , r ]内的颜色取反
0 l r 计算[ l , r ]内最长连续黑色石头的个数

明显的线段树区间合并,记录lmax(从左端点开始的最长值) rmax(从右端点开始的最长值) 用于更新mmax(区间最长值)
但是这儿有区间更新,所以记录0的三个最长值和1的三个最长值,更新父节点的时候交换0与1就好。
还有这儿注意查询时,可能值在查询的几个子区间的的相邻处(因为我们只能查询子区间内的最大值,而答案却是几个子区间合在一起的其中某一段)。我们可以根据代码看出,区间查询时一定是从左端点依次不重不漏的进入几个子区间到右端点,所以我们计算最大值时就使用左边的rmax加上右边的lmax。具体看代码

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define eps 1E-8
/*注意可能会有输出-0.000*/
#define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型
#define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化
#define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0
#define mul(a,b) (a<<b)
#define dir(a,b) (a>>b)
typedef long long ll;
typedef unsigned long long ull;
const int Inf=1<<28;
const double Pi=acos(-1.0);
const int Max=100010<<2;
struct node
{
    int llmax,lrmax,lmmax;
    int olmax,ormax,ommax;//最长的0个数 用于区间更新
}segtr[Max];
int upn[Max],tem;
int nmax(int a,int b)
{
    return a>b?a:b;
}
void Upnow(int now,int next,int sum)//区间更新
{
    segtr[now].lmmax=nmax(nmax(segtr[next].lmmax,segtr[next|1].lmmax),segtr[next].lrmax+segtr[next|1].llmax);

    segtr[now].llmax=segtr[next].llmax;
    if(segtr[next].llmax==sum-(sum>>1))
        segtr[now].llmax+=segtr[next|1].llmax;

        segtr[now].lrmax=segtr[next|1].lrmax;
    if(segtr[next|1].lrmax==(sum>>1))
        segtr[now].lrmax+=segtr[next].lrmax;

         segtr[now].ommax=nmax(nmax(segtr[next].ommax,segtr[next|1].ommax),segtr[next].ormax+segtr[next|1].olmax);

    segtr[now].olmax=segtr[next].olmax;
    if(segtr[next].olmax==sum-(sum>>1))
        segtr[now].olmax+=segtr[next|1].olmax;

        segtr[now].ormax=segtr[next|1].ormax;
    if(segtr[next|1].ormax==(sum>>1))
        segtr[now].ormax+=segtr[next].ormax;
    return;
}
void Create(int sta,int enn,int now)
{
    upn[now]=0;
    if(sta==enn)
    {
        scanf("%d",&tem);
        if(tem)//开始tem为1
        {
            segtr[now].ommax=segtr[now].olmax=segtr[now].ormax=0;
            segtr[now].lmmax=segtr[now].llmax=segtr[now].lrmax=1;
        }
        else
        {
            segtr[now].ommax=segtr[now].olmax=segtr[now].ormax=1;
            segtr[now].lmmax=segtr[now].llmax=segtr[now].lrmax=0;
        }
        return;
    }
    int mid=dir(sta+enn,1);
    int next=mul(now,1);
    Create(sta,mid,next);
    Create(mid+1,enn,next|1);
    Upnow(now,next,enn-sta+1);
    return;
}
void Swap(int &a,int &b)
{
    int t=a;
    a=b;
    b=t;
    return;
}
void Cha(int now)//交换此段0与1的个数
{
    Swap(segtr[now].lmmax,segtr[now].ommax);
    Swap(segtr[now].llmax,segtr[now].olmax);
    Swap(segtr[now].lrmax,segtr[now].ormax);
    return;
}
void Downow(int now)
{
    if(upn[now])
    {
        int next=mul(now,1);
        upn[next]=(1+upn[next])%2;
        upn[next|1]=(1+upn[next|1])%2;
        Cha(next);
        Cha(next|1);
        upn[now]=0;
    }
    return;
}
int ans,mmid;//最终结果 没有断开就继续加
void Update(int sta,int enn,int now,int x,int y,int z)//注意计算结果时虽然有回溯过程,但是一定是从**满足条件的最左边运行到最右边结束**
{
    if(sta>=x&&enn<=y)
    {
        if(z)
        {
            upn[now]=(upn[now]+1)%2;
            Cha(now);
        }
        else//**关键是两边区间有连接的情况**
        {
                    mmid+=segtr[now].llmax;
                ans=nmax(nmax(ans,mmid),segtr[now].lmmax);
                if(segtr[now].llmax==enn-sta+1);//整个区间相连
               else
                mmid=segtr[now].lrmax;
        }
        return;
    }
    Downow(now);
    int mid=dir(sta+enn,1);
    int next=mul(now,1);
    if(mid>=x)
      Update(sta,mid,next,x,y,z);
    if(mid<y)
         Update(mid+1,enn,next|1,x,y,z);
        Upnow(now,next,enn-sta+1);
    return;
}
int main()
{
    int n,m;
    int p,lef,rig;
    while(~scanf("%d",&n))
    {
        Create(1,n,1);
        scanf("%d",&m);
        while(m--)
        {
            scanf("%d %d %d",&p,&lef,&rig);
            if(!p)
            {
                mmid=ans=0;
                Update(1,n,1,lef,rig,0);
                printf("%d\n",ans);
            }
            else
            Update(1,n,1,lef,rig,1);
        }
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值