题解[SHOI2015]脑洞治疗仪

Posted Refined_heart

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解[SHOI2015]脑洞治疗仪相关的知识,希望对你有一定的参考价值。

Problem

\\(\\text{Solution:}\\)

这题唯一需要学习 or 复习的点就是它的查询了。

这东西一眼的维护左右最长连续的 \\(0\\) 的长度就做完了。标记什么的都很简单。代码量略微大一点。

注意在询问的时候:

如果完全在左右区间,就分别递归。

否则,我们还需要考虑跨越区间的最值。那应该是:从当前向左右两侧延申最长距离之和。

这个自己推一下就行了。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+10;
int ls[MAXN],rs[MAXN],sum[MAXN];
int node,opt;
int llen[MAXN],rlen[MAXN],mlen[MAXN];
int L[MAXN],R[MAXN],tag[MAXN],n,m,rt;
inline int read() {
	int s=0;
	char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) {
		s=s*10-48+ch;
		ch=getchar();
	}
	return s;
}
inline int Len(int x) {
	return R[x]-L[x]+1;
}
inline int Min(int x,int y) {
	return x<y?x:y;
}
inline int Max(int x,int y) {
	return x>y?x:y;
}
inline void pushup(int x) {
	sum[x]=sum[ls[x]]+sum[rs[x]];
	llen[x]=llen[ls[x]];
	if(llen[ls[x]]==Len(ls[x]))
		llen[x]+=llen[rs[x]];
	
	rlen[x]=rlen[rs[x]];
	if(rlen[x]==Len(rs[x]))
		rlen[x]+=rlen[ls[x]];
	
	mlen [ x ] = Max ( mlen [ ls [ x ] ] , mlen [ rs [ x ] ] ) ;
	mlen [ x ] = Max ( mlen [ x ] , rlen [ ls [ x ] ] + llen [ rs [ x ] ] ) ;
}
void build(int &x,int l,int r) {
	if(!x)x=++node;
	L[x]=l;
	R[x]=r;
	tag[x]=-1;
	if(l==r) {
		rlen[x]=llen[x]=mlen[x]=0;
		sum[x]=1;
		return;
	}
	int mid=(l+r)>>1;
	build(ls[x],l,mid);
	build(rs[x],mid+1,r);
	pushup(x);
}
inline void pushdown(int x) {
	if(tag[x]!=-1) {
		int p=tag[x];
		tag[x]=-1;
		tag[ls[x]]=tag[rs[x]]=p;
		sum[ls[x]]=p*Len(ls[x]);
		sum[rs[x]]=p*Len(rs[x]);
		if(p==0) {
			llen[ls[x]]=rlen[ls[x]]=mlen[ls[x]]=Len(ls[x]);
			llen[rs[x]]=rlen[rs[x]]=mlen[rs[x]]=Len(rs[x]);
		} else {
			llen[ls[x]]=rlen[ls[x]]=mlen[ls[x]]=0;
			llen[rs[x]]=rlen[rs[x]]=mlen[rs[x]]=0;
		}
	}
}
int query(int x,int l,int r) {
	if(L[x]>=l&&R[x]<=r)return sum[x];
	pushdown(x);
	int mid=(L[x]+R[x])>>1,res=0;
	if(l<=mid)res=query(ls[x],l,r);
	if(mid<r)res+=query(rs[x],l,r);
	pushup(x);
	return res;
}
void change(int x,int l,int r,int v) {
	if(L[x]>=l&&R[x]<=r) {
		tag[x]=v;
		sum[x]=v*Len(x);
		llen[x]=rlen[x]=mlen[x]=(1-v)*Len(x);
		return;
	}
	pushdown(x);
	int mid=(L[x]+R[x])>>1;
	if(l<=mid)change(ls[x],l,r,v);
	if(mid<r)change(rs[x],l,r,v);
	pushup(x);
}
int query_max(int x,int l,int r) {
	if(L[x]>=l&&R[x]<=r)return mlen[x];
	pushdown(x);
	int mid=(L[x]+R[x])>>1;
	if(r<=mid)return query_max(ls[x],l,r);
	else if(l>mid)return query_max(rs[x],l,r);
	return Max(Max(query_max(ls[x],l,r),query_max(rs[x],l,r)),Min(rlen[ls[x]],L[rs[x]]-l)+Min(llen[rs[x]],r-R[ls[x]]));
}
int querypos(int l,int r,int s) {
	int L=l,R=r,ans=l;
	while(L<=R){
		int mid=(L+R)>>1;
		if(mid-l+1-(query(rt,l,mid))<=s)ans=mid,L=mid+1;
		else R=mid-1;
	}
	return ans;
}
//void dfs(int x){
//	pushdown(x);
//	if(!ls[x]&&!rs[x])printf("%d ",sum[x]);
//	if(ls[x])dfs(ls[x]);
//	if(rs[x])dfs(rs[x]);
//}
inline void write(int x){
	if(x>9)write(x/10);
	putchar(x%10+48);
}
int main() {
	n=read();
	m=read();
	build(rt,1,n);
	while(m--) {
		opt=read();
		int al=read(),ar=read(),bl,br;
		if(opt==0)change(rt,al,ar,0);
		else if(opt==1) {
			bl=read();
			br=read();
			int S=query(rt,al,ar);
			change(rt,al,ar,0);
			int P=query(rt,bl,br);
			int cnt0=(br-bl+1)-P;
			S=Min(S,cnt0);
			if(S==0)continue;
			int pos=querypos(bl,br,S);
			change(rt,bl,pos,1);
		} else write(query_max(rt,al,ar)),putchar(\'\\n\');
	}
	return 0;
}

以上是关于题解[SHOI2015]脑洞治疗仪的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4592[Shoi2015]脑洞治疗仪 线段树

LOJ #2037. 「SHOI2015」脑洞治疗仪

[SHOI2015]脑洞治疗仪

[SHOI2015]脑洞治疗仪(恶心的线段树,区间最大子段和)

SHOI2015脑洞治疗仪(恶心的线段树,区间最大子段和)

[SHOI 2015] 脑洞治疗仪