BZOJ1858&&洛谷P2572 [SCOI2010]序列操作

毒瘤线段树,调了一下午+一晚上%%%hzwer大爷

我们需要维护

zero[0]:从当前区间左端点开始有多少个连续的0     one[0]:从当前区间左端点开始有多少个连续的1

zero[1]:从当前区间右端点开始有多少个连续的0     one[1]:从当前区间右端点开始有多少个连续的1  

zero[2]:当前区间最长的连续的0的个数                    one[2]:当前区间最长的连续的1的个数 

sum[0]:当前区间0的个数                                         sum[1]:当前区间1的个数

lazy=-1:当前区间没有全部为1或0的操作

lazy=0:当前区间要全部换成0

lazy=1:当前区间要全部换成1

f=0/1 :区间不需要/需要进行翻转操作

full=-1/0/1:当前区间既有0也有1/只有0/只有1

然后我们考虑如何更新,大区间的左端点的值用左儿子更新,右端点的值用右儿子更新,区间最值在两个儿子中取max

然后考虑几个特殊情况,ls.full=0,也就是说左儿子全是0,那么大区间的zero[0]就可以加上右儿子的zero[0]因为这两段连起来了

同理ls.full=1的情况也要加上右儿子的one[1],右儿子同理

区间最值   

zero[0]=max{ls.zero[0],rs.zero[0],ls.zero[1]+rs.zero[0]}就是说在两个子区间的最大值与中间连续一段的长度中取max

lazy操作

lazy的优先级要大于翻转的优先级,一旦有了lazy,那么翻转就要清空

全置成0的话zero[0]=zero[1]=zero[2]=sum[0]=len(区间长),1也同理,但我们要同时更新,那就:

zero[0]=zero[1]=zero[2]=sum[0]=len*(lazy^1)       one[0]=one[1]=one[2]=sum[1]=len*lazy   (自己感性理解为什么

记得清空翻转操作和更新full标记

f   翻转操作

翻转就是0和1互换嘛,相应的zero 和  one也互换一下就好了,还有 full标记

查询区间和就是1的个数,最长连续1就递归在左右区间取max一直更新就吼啦!

代码

//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream> 
#include<algorithm>
#define ls x<<1
#define rs x<<1|1
#define lb t[x].b[0]
#define rb t[x].b[1]
using namespace std;
const int M=500500;
struct Seg
{
	int b[2],z[3];
	int la,f,o[3];
	int s[2],full;
	Seg(){b[0]=b[1]=z[0]=z[1]=z[2]=o[0]=o[1]=o[2]=s[0]=s[1]=f=0;la=full=-1;}
}t[M];
int n,m;
inline Seg up(Seg x,Seg y)
{
	Seg e;e.f=0;e.la=-1;
	e.b[0]=x.b[0]; e.b[1]=y.b[1]; 
	e.z[0]=x.z[0]; e.z[1]=y.z[1]; 
	e.o[0]=x.o[0]; e.o[1]=y.o[1];
	e.z[2]=max(x.z[2],y.z[2]);
	e.o[2]=max(x.o[2],y.o[2]);
	e.z[2]=max(e.z[2],x.z[1]+y.z[0]);
	e.o[2]=max(e.o[2],x.o[1]+y.o[0]);
	e.s[0]=x.s[0]+y.s[0];e.s[1]=x.s[1]+y.s[1];
	if(x.full==0) e.z[0]=x.z[2]+y.z[0];
    if(x.full==1) e.o[0]=x.o[2]+y.o[0];
    if(y.full==0) e.z[1]=y.z[2]+x.z[1];
    if(y.full==1) e.o[1]=y.o[2]+x.o[1];
    e.full=(x.full==y.full)?x.full:-1;
	return e;
}
inline void cover(int x,int v)
{
	t[x].f=0;t[x].la=v;int len=rb-lb+1;t[x].full=v;
	t[x].s[1]=t[x].o[0]=t[x].o[1]=t[x].o[2]=v*len;
	t[x].s[0]=t[x].z[0]=t[x].z[1]=t[x].z[2]=(v^1)*len;
	return ;
}
inline void rever(int x)
{
	swap(t[x].z[0],t[x].o[0]);swap(t[x].o[1],t[x].z[1]);
	swap(t[x].z[2],t[x].o[2]);swap(t[x].s[0],t[x].s[1]);
	t[x].f^=1;if (t[x].full!=-1) t[x].full^=1;
	return ;
}
inline void down(int x)
{
	if (lb==rb) return ;
	if (t[x].la!=-1) 
		cover(ls,t[x].la),cover(rs,t[x].la),t[x].la=-1;
	if (t[x].f)
		rever(ls),rever(rs),t[x].f=0;
	return ;
}
inline void built(int l,int r,int x)
{
	t[x].b[0]=l;t[x].b[1]=r;t[x].la=-1;
	if (l==r)
	{
		scanf("%d",&t[x].full);
		t[x].o[0]=t[x].o[1]=t[x].o[2]=t[x].s[1]=t[x].full;
		t[x].z[0]=t[x].z[1]=t[x].z[2]=t[x].s[0]=t[x].full^1;
		return ;
	}
	int mid=(l+r)>>1;
	built(l,mid,ls);built(mid+1,r,rs);
	return (void)(t[x]=up(t[ls],t[rs]));
}
inline void update(int l,int r,int x,int v)
{
	down(x);
	if (l<=lb&&rb<=r) return (void)(cover(x,v));	
	int mid=(lb+rb)>>1;
	if (l<=mid) update(l,r,ls,v);
	if (r>mid) update(l,r,rs,v);
	return (void)(t[x]=up(t[ls],t[rs]));
} 
inline void upturn(int l,int r,int x)
{
	down(x);
	if (l<=lb&&rb<=r)
	{
		rever(x),t[x].f=1;
		return ;
	}
	int mid=(lb+rb)>>1;
	if (l<=mid) upturn(l,r,ls);
	if (r>mid) upturn(l,r,rs);
	return (void)(t[x]=up(t[ls],t[rs]));
}
inline int asks(int l,int r,int x)
{
	down(x);
	if (l<=lb&&rb<=r) return t[x].s[1];
	int mid=(lb+rb)>>1;int ans=0;
	if (l<=mid) ans+=asks(l,r,ls);
	if (r>mid) ans+=asks(l,r,rs);
	return ans;
}
inline Seg ask1(int l,int r,int x)
{
	down(x);
	if (l<=lb&&rb<=r) return t[x];
	int mid=(lb+rb)>>1;Seg jun,er;
	if (l<=mid) jun=ask1(l,r,ls);
	if (r>mid) er=ask1(l,r,rs);
	return up(jun,er);
}
signed main()
{
	scanf("%d%d",&n,&m);
	int fl,x,y;built(1,n,1);
	while (m--)
	{
		scanf("%d%d%d",&fl,&x,&y);x++;y++;
		if (fl==0) update(x,y,1,0);
		else if (fl==1) update(x,y,1,1);
		else if (fl==2) upturn(x,y,1);
		else if (fl==3) printf("%d\n",asks(x,y,1));
		else if (fl==4) printf("%d\n",ask1(x,y,1).o[2]);
	}
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值