题解 CF1371F Raging Thunder(线段树,分类讨论)

题目链接

首先,不难发现,对于每个形如>>>...>><<...<<<的结构,球会掉到中间的那个洞里。我们可以形象地把>看做向下的坡,<看做向上的坡,那么这个球运动的情况,就是顺着坡向下滚,很好理解。我们把每个这样的(下坡再上坡的)结构,称作一个“坑”,那么题目就是要询问一段区间里,“最大的坑”的大小。

因为是区间操作,区间查询,我们考虑用线段树维护。

具体来说,对线段树上一个区间,我们维护三个东西:

  • 这段区间最前面的一个坑(也可能是半个坑,也就是只有下坡或只有上坡)。
  • 这段区间最后面的一个坑(也可能是半个坑;也可能根本不存在,因为整段区间就只有一个坑,已经记录在上一条里了)。
  • 这段区间,中间(不算最前面、最后面的坑),最大的坑的大小。

例如:><<>><>><<>>>>这段区间。

  • 最前面的坑,就是><<。我们记录的时候,记录它的下坡长度:\(1\),上坡长度:\(2\)
  • 最后面的坑,就是>>>>。我们记录的时候,记录它的下坡长度:\(4\),上坡长度:\(0\)
  • 中间的,就是指>><>><<这段,有:>><>><<两个坑。其中最大的是第二个,大小为\(4\)。我们就记录这个大小:\(4\)

接下来,我们要重点解决两个问题。

问题一,是区间怎么合并?也就是在线段树上,当前区间有一左一右两个儿子,怎么把它们的信息合并起来(\(\texttt{push_up}\))呢?发现重点,在于处理中间的拼接情况,也就是左儿子的“后面的坑”,和右儿子的“前面的坑”,如何结合。结合后,可能新产生\(0\), \(1\)\(2\)个“中间的坑”,也可能会影响到合并后的区间的“前面的坑”或“后面的坑”的形态。大力分类讨论即可。

问题二,是如何实现区间反转?我们可以在一开始建线段树的时候,就处理好“反转前”和“反转后”两套信息。当需要把一个区间反转时,就把这两套信息交换一下即可。

时间复杂度\(O(n+q\log n)\)

参考代码(分类讨论写的比较麻烦,建议读者自己实现):

//韩信带净化,只有你们想不到的,没有毛队做不到的
//大力讨论就完事了,奥利给!!!
//problem:CF1371F
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

const int MAXN=5e5;
int n,q;
char s[MAXN+5];

struct RangeInfo{
	int left_down,left_up;
	int right_down,right_up;
	int ans;//除去头尾后的最大答案
	RangeInfo(){
		left_down=left_up=right_down=right_up=ans=0;
	}
};
RangeInfo merge(const RangeInfo& a,const RangeInfo& b){
	RangeInfo res;
	res.left_down=a.left_down;
	res.left_up=a.left_up;
	res.right_down=b.right_down;
	res.right_up=b.right_up;
	res.ans=max(a.ans,b.ans);
	
	if(a.right_up){
		assert(a.right_down!=0);
		if(b.left_down){
			res.ans=max(res.ans,a.right_down+a.right_up);
			if(b.left_up){
				if(b.right_down)res.ans=max(res.ans,b.left_down+b.left_up);
				else{
					res.right_down=b.left_down;
					res.right_up=b.left_up;
				}
			}
			else{
				res.right_down=b.left_down;
			}
		}
		else{
			assert(b.left_up!=0);
			if(b.right_down){
				res.ans=max(res.ans,a.right_down+a.right_up+b.left_up);
			}
			else{
				res.right_down=a.right_down;
				res.right_up=a.right_up+b.left_up;
			}
		}
	}
	else{
		if(a.right_down){
			if(b.left_down){
				if(b.left_up){
					if(b.right_down)res.ans=max(res.ans,a.right_down+b.left_down+b.left_up);
					else{
						res.right_down=a.right_down+b.left_down;
						res.right_up=b.left_up;
					}
				}
				else{
					res.right_down=a.right_down+b.left_down;
				}
			}
			else{
				assert(b.left_up!=0);
				if(b.right_down)res.ans=max(res.ans,a.right_down+b.left_up);
				else{
					res.right_down=a.right_down;
					res.right_up=b.left_up;
				}
			}
		}
		else{
			if(a.left_down){
				if(a.left_up){
					if(b.left_down){
						if(b.left_up){
							if(b.right_down)res.ans=max(res.ans,b.left_down+b.left_up);
							else{
								res.right_down=b.left_down;
								res.right_up=b.left_up;
							}
						}
						else{
							res.right_down=b.left_down;
						}
					}
					else{
						assert(b.left_up!=0);
						res.left_up=a.left_up+b.left_up;
					}
				}
				else{
					res.left_down=a.left_down+b.left_down;
					res.left_up=b.left_up;
				}
			}
			else{
				assert(a.left_up!=0);
				if(b.left_down){
					if(b.left_up){
						if(b.right_down)res.ans=max(res.ans,b.left_down+b.left_up);
						else{
							res.right_down=b.left_down;
							res.right_up=b.left_up;
						}
					}
					else{
						res.right_down=b.left_down;
					}
				}
				else{
					assert(b.left_up!=0);
					res.left_up=a.left_up+b.left_up;
				}
			}
		}
	}
	return res;
}
int getans(const RangeInfo& a){
	int ans=a.ans;
	ans=max(ans,a.left_down+a.left_up);
	ans=max(ans,a.right_down+a.right_up);
	return ans;
}
struct SegmentTree{
	RangeInfo cur[MAXN*4+5],rev[MAXN*4+5];
	bool tag[MAXN*4+5];
	void push_up(int p){
		cur[p]=merge(cur[p<<1],cur[p<<1|1]);
		rev[p]=merge(rev[p<<1],rev[p<<1|1]);
	}
	void build(int p,int l,int r){
		if(l==r){
			if(s[l]=='>'){
				cur[p].left_down=1;
				rev[p].left_up=1;
			}
			else{
				cur[p].left_up=1;
				rev[p].left_down=1;
			}
			return;
		}
		int mid=(l+r)>>1;
		build(p<<1,l,mid);
		build(p<<1|1,mid+1,r);
		push_up(p);
	}
	void upd(int p){
		tag[p]^=1;
		swap(cur[p],rev[p]);
	}
	void push_down(int p){
		if(tag[p]){
			upd(p<<1);
			upd(p<<1|1);
			tag[p]=0;
		}
	}
	void modify(int p,int l,int r,int ql,int qr){
		//区间反转
		if(ql<=l && qr>=r){
			upd(p);
			return;
		}
		int mid=(l+r)>>1;
		push_down(p);
		if(ql<=mid)modify(p<<1,l,mid,ql,qr);
		if(qr>mid)modify(p<<1|1,mid+1,r,ql,qr);
		push_up(p);
	}
	RangeInfo Q_res;
	bool flag;
	void query(int p,int l,int r,int ql,int qr){
		if(ql<=l && qr>=r){
			if(!flag){
				Q_res=cur[p];
				flag=1;
			}
			else Q_res=merge(Q_res,cur[p]);
			return;
		}
		int mid=(l+r)>>1;
		push_down(p);
		if(ql<=mid)query(p<<1,l,mid,ql,qr);
		if(qr>mid)query(p<<1|1,mid+1,r,ql,qr);
		push_up(p);
	}
	RangeInfo query(int l,int r){
		Q_res=RangeInfo();
		flag=0;
		query(1,1,n,l,r);
		return Q_res;
	}
}T;

int main() {
	cin>>n>>q;
	cin>>(s+1);
	T.build(1,1,n);
	while(q--){
		int l,r;cin>>l>>r;
		T.modify(1,1,n,l,r);
		RangeInfo res=T.query(l,r);
		cout<<getans(res)<<endl;
	}
	return 0;
}
posted @ 2020-07-02 14:11  duyiblue  阅读(403)  评论(0编辑  收藏  举报