hdu3911 线段树

最近一直在做线段树题     感觉都差不多  

这道题关键在于每个节点存了多个值    对每个节点

pre0 pre1表示节点前缀连续最大的0 1个数

after0,after1表示节点后缀连续最大的0 1个数

Max0 Max1表示节点表示区间连续的最大0 1个数

flash表示节点的子节点知否需要更新(就是所谓的延时)


#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
#define LL(x) (x<<1)
#define RR(x) ((x<<1)|1)


struct node
{
int pre0,pre1,after0,after1;
int Max0,Max1,flash;
}num[4*100000];
int max(int a,int b)
{
return a>b?a:b;
}
int min(int a,int b)
{
return a<b?a:b;
}
int deal(int L,int R,int mark)
{
num[mark].pre1=num[mark].after1=num[mark].Max1=R-L+1;
num[mark].pre0=num[mark].after0=num[mark].Max0=num[mark].flash=0;
if(L==R) return 0;
int mid=(L+R)/2;
deal(L,mid,LL(mark));
deal(mid+1,R,RR(mark));
return 0;
}
int change(int a)
{
int k=num[a].pre0;
num[a].pre0=num[a].pre1;
num[a].pre1=k;
k=num[a].after0;
num[a].after0=num[a].after1;
num[a].after1=k;
k=num[a].Max0;
num[a].Max0=num[a].Max1;
num[a].Max1=k;
num[a].flash=!num[a].flash;
return 0;
}//更新给个节点的状态
int update(int L,int R,int left,int right,int mark)
{
if(L==left&&R==right)
{
change(mark);
return 0;
}
if(num[mark].flash)
{
change(LL(mark));
change(RR(mark));
num[mark].flash=0;
}//延时判断
int mid=(L+R)/2;
if(right<=mid)
{
update(L,mid,left,right,LL(mark));
}
else if(left>mid)
{
update(mid+1,R,left,right,RR(mark));
}
else
{
update(L,mid,left,mid,LL(mark));
update(mid+1,R,mid+1,right,RR(mark));
}
num[mark].Max0=max(num[LL(mark)].Max0,num[RR(mark)].Max0);//最长只可能有3种情况 全在左子节点 全在右子节点   在中间
num[mark].Max0=max(num[LL(mark)].after0+num[RR(mark)].pre0,num[mark].Max0);
num[mark].Max1=max(num[LL(mark)].Max1,num[RR(mark)].Max1);
num[mark].Max1=max(num[LL(mark)].after1+num[RR(mark)].pre1,num[mark].Max1);
num[mark].pre0=num[LL(mark)].pre0;
if(num[LL(mark)].pre0==mid-L+1) num[mark].pre0+=num[RR(mark)].pre0;
num[mark].pre1=num[LL(mark)].pre1;
if(num[LL(mark)].pre1==mid-L+1) num[mark].pre1+=num[RR(mark)].pre1;
num[mark].after0=num[RR(mark)].after0;
if(num[RR(mark)].after0==R-mid) num[mark].after0+=num[LL(mark)].after0;
num[mark].after1=num[RR(mark)].after1;
if(num[RR(mark)].after1==R-mid) num[mark].after1+=num[LL(mark)].after1;
return 0;
}
int find(int L,int R,int left,int right,int mark)
{
int mid=(L+R)/2;
if(L==left&&R==right)
{
return num[mark].Max1;
}
if(num[mark].flash)
{
change(LL(mark));
change(RR(mark));
num[mark].flash=0;
}
if(right<=mid)
{
return find(L,mid,left,right,LL(mark));
}
else if(left>mid)
{
return find(mid+1,R,left,right,RR(mark));
}
else
{
int t1=find(L,mid,left,mid,LL(mark));
int t2=find(mid+1,R,mid+1,right,RR(mark));
return max(max(min(num[LL(mark)].after1, t1) + min(num[RR(mark)].pre1, t2), t1), t2);//这个地方不咋好懂    就是最大的连续1只可能出现在左zhi
}
return 0;
}
int main()
{
int n,i,j,a,b,c,m;
while(~scanf("%d",&n))
{
//memset(num,0,sizeof(num));
deal(1,n,1);//先把整棵树初始化,我是认为所有石头开始全为黑 
for(i=1;i<=n;i++)
{
scanf("%d",&a);
if(a==0) update(1,n,i,i,1);//如果为白 就需要更新
}
scanf("%d",&m);
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&c);
if(a)
{
update(1,n,b,c,1);
}
else
{
printf("%d\n",find(1,n,b,c,1));
}
}
}
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值