码迷,mamicode.com
首页 > 其他好文 > 详细

【题解】[SHOI2015]脑洞治疗仪

时间:2021-07-05 17:12:33      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:main   长度   ret   tag   lang   pac   getchar   代码   完全   

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]脑洞治疗仪

标签:main   长度   ret   tag   lang   pac   getchar   代码   完全   

原文地址:https://www.cnblogs.com/h-lka/p/14965297.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!