2020.01.23日常总结

P 2572      [ S C O I 2010 ] \color{green}{洛谷P2572\ \ \ \ [SCOI2010]序列操作}

\color{blue}{【简明题意】:}
在这里插入图片描述
\color{blue}{【思路】:} 像这种维护序列的题目,一般都可以用 线 \color{orange}{线段树} 求解。

首先,我们来看看我们需要定义一些什么东西:
在这里插入图片描述
其实,本题的操作都不算很难,最难的操作是求区间内最长的连续的 1 1 的个数。下面,我们来讲讲此操作。

首先,我们可以把问题拆分成以下几个情况:

  • 查询区间在当前访问区间的中点之前。我们直接返回当前区间的左子区间即可。
  • 查询区间在当前访问区间的中点之后。我们直接返回当前区间的右子区间即可。
  • 查询区间跨越了当前访问区间的中点两边。我们先查询分别左右子区间的答案,记为 a n s 1 , a n s 2 ans_1,ans_2 。当然答案可能也跨越了中点两边,我们重点考虑这种情况。我们可以很自然的想到,因为题目要求连续,所以自然它由左子区间的右边的连续的 1 1 和右子区间的左边的连续的 1 1 构成。但是,有这么一种情况:即左子区间的右边的连续的 1 1 的数量大于查询区间在左子区间的长度。所以我们需要在两者间取最小值。

讲的有点隐晦,我们来看代码吧:

#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
	char c=0;int x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}//以上是卡常用的快读 
const int N=1e5+100;
int sumo[N<<2],sumz[N<<2],cono[N<<2];
int conz[N<<2],lz[N<<2],add[N<<2];
int rz[N<<2],lo[N<<2],ro[N<<2],len[N<<2];
//sumo:区间内1的个数
//sumz:区间内0的个数
//cono:区间内最多连续1的个数
//conz:区间内最多连续0的个数
// add:区间修改的懒标记(1:全改1;2:全改0;3:取反;0:不操作) 
//  lz:区间内左边连续0的个数
//  rz:区间内右边连续0的个数
//  lo:区间内左边连续1的个数
//  ro:区间内右边连续1的个数 
inline void pushup(int o){
	sumo[o]=sumo[o<<1]+sumo[o<<1|1];sumz[o]=sumz[o<<1]+sumz[o<<1|1];
	cono[o]=max(cono[o<<1],max(cono[o<<1|1],ro[o<<1]+lo[o<<1|1]));
	conz[o]=max(conz[o<<1],max(conz[o<<1|1],rz[o<<1]+lz[o<<1|1]));
	if (lo[o<<1]==len[o<<1])
		lo[o]=lo[o<<1]+lo[o<<1|1];
	else lo[o]=lo[o<<1];
	if (ro[o<<1|1]==len[o<<1|1])
		ro[o]=ro[o<<1]+ro[o<<1|1];
	else ro[o]=ro[o<<1|1];
	if (lz[o<<1]==len[o<<1])
		lz[o]=lz[o<<1]+lz[o<<1|1];
	else lz[o]=lz[o<<1];
	if (rz[o<<1|1]==len[o<<1|1])
		rz[o]=rz[o<<1]+rz[o<<1|1];
	else rz[o]=rz[o<<1|1];
}
inline void inverse(int o){
	add[o]=3-add[o];
	swap(sumo[o],sumz[o]);
	swap(cono[o],conz[o]);
	swap(lz[o],lo[o]);
	swap(rz[o],ro[o]);
}//区间的取反操作
inline void change_to_1(int o){
	sumo[o]=cono[o]=lo[o]=ro[o]=len[o];
	sumz[o]=conz[o]=lz[o]=rz[o]=0;
}//把区间o的所有数改为1
inline void change_to_0(int o){
	sumo[o]=cono[o]=lo[o]=ro[o]=0;
	sumz[o]=conz[o]=lz[o]=rz[o]=len[o];
}//把区间o的所有数改为0
inline void pushdown(int o){
	int tag=add[o];add[o]=0;
	if (tag==1){
		change_to_1(o<<1);add[o<<1|1]=1;
		change_to_1(o<<1|1);add[o<<1]=1;
	}
	else if (tag==2){
		change_to_0(o<<1);add[o<<1|1]=2;
		change_to_0(o<<1|1);add[o<<1]=2;
	}
	else if (tag==3){
		inverse(o<<1);
		inverse(o<<1|1);
	}
}//标记下传操作
bool a[N];int n,m;
void build(int o,int l,int r){
	add[o]=0;len[o]=r-l+1;
	if (l==r){
		if (a[l]) change_to_1(o);
		else change_to_0(o);
		return;
	}
	register int mid=(l+r)>>1;
	build(o<<1,l,mid);
	build(o<<1|1,mid+1,r);
	pushup(o);return;
}//建树操作
void updata1(int o,int l,int r,int p,int q){
	if (l>q||r<p) return;
	if (p<=l&&r<=q){
		change_to_1(o);
		add[o]=1;return;
	}
	if (add[o]) pushdown(o);
	register int mid=(l+r)>>1;
	updata1(o<<1,l,mid,p,q);
	updata1(o<<1|1,mid+1,r,p,q);
	pushup(o);return;
}//操作2:把区间所有的数改为1
void updata2(int o,int l,int r,int p,int q){
	if (l>q||r<p) return;
	if (p<=l&&r<=q){
		change_to_0(o);
		add[o]=2;return;
	}
	if (add[o]) pushdown(o);
	register int mid=(l+r)>>1;
	updata2(o<<1,l,mid,p,q);
	updata2(o<<1|1,mid+1,r,p,q);
	pushup(o);return;
}//操作1:把区间所有的数改为0
void updata3(int o,int l,int r,int p,int q){
	if (l>q||r<p) return;
	if (p<=l&&r<=q){
		inverse(o);
		return;
	}
	if (add[o]) pushdown(o);
	register int mid=(l+r)>>1;
	updata3(o<<1,l,mid,p,q);
	updata3(o<<1|1,mid+1,r,p,q);
	pushup(o);return;
}//操作3:区间取反操作
int query1(int o,int l,int r,int p,int q){
	if (l>q||r<p) return 0;
	if (p<=l&&r<=q) return sumo[o];
	if (add[o]) pushdown(o);
	int mid=(l+r)>>1,ans=0;
	ans+=query1(o<<1,l,mid,p,q);
	ans+=query1(o<<1|1,mid+1,r,p,q);
	return ans;
}//操作4:查询区间有多少个1
int query2(int o,int l,int r,int p,int q){
	if (l>q||r<p) return 0;
	if (p<=l&&r<=q) return cono[o];
	if (add[o]) pushdown(o);
	register int mid=(l+r)>>1,answer=0;
	if (q<=mid) answer=query2(o<<1,l,mid,p,q);
	else if (p>mid) answer=query2(o<<1|1,mid+1,r,p,q);
	else{
		answer=query2(o<<1|1,mid+1,r,p,q);
		answer=max(answer,query2(o<<1,l,mid,p,q));
		int Ro=min(ro[o<<1],mid-p+1);
		int Lo=min(lo[o<<1|1],q-mid);
		answer=max(answer,Ro+Lo);
	}
	return answer;
}//操作5:查询区间最长连续1的个数
//以上全部都是线段树的维护与查询 
int main(){
	freopen("t1.in","r",stdin);
	n=read();m=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int opt=read(),l=read()+1,r=read()+1;
		switch(opt){
			case 0:updata2(1,1,n,l,r);break;
			case 1:updata1(1,1,n,l,r);break;
			case 2:updata3(1,1,n,l,r);break;
			case 3:printf("%d\n",query1(1,1,n,l,r));break;
			default:printf("%d\n",query2(1,1,n,l,r));
		}
	}
	return 0;
}
发布了82 篇原创文章 · 获赞 4 · 访问量 1756

猜你喜欢

转载自blog.csdn.net/ZHUYINGYE_123456/article/details/104075560