0
点赞
收藏
分享

微信扫一扫

[ACNOI2022]模拟倒水

诗远 2022-02-21 阅读 22
c++

题目

题目背景
“你见过我的全盛时期吗?” D D ( X Y X ) \sf DD(XYX) DD(XYX) 冷笑着,一脚将 O n e I n D a r k \sf OneInDark OneInDark 踢飞,“你也想起舞吗?”

“能阻止我的生物只有一个——千手嘟间!就凭这个——”

完全体丶须佐科夫!”

题目描述
给出一个括号序列,每次询问一个子区间 [ l , r ] [l,r] [l,r]:请在其中找出最长的子区间构成合法括号序列。

数据范围与提示
n ⩽ 1 0 5 n\leqslant 10^5 n105,但 那挨千刀的坏东西出题人设置 m ⩽ 4 × 1 0 6 m\leqslant 4\times 10^6 m4×106 。时限 2 s 2\rm s 2s,空间限制 512 M 512\rm M 512M

思路

我的做法也是 O ( m log ⁡ n ) \mathcal O(m\log n) O(mlogn),但是常数大,所以过不了。简单来讲,猫树。

并非所有区间询问都需要线段树 / / / 猫树。哪怕渐进时间复杂度是对的,常数也不会允许……空间复杂度、代码复杂度则劣多了……

基本题意转化是,规定 ( − 1 -1 1) + 1 +1 +1,求前缀和,则目标为找到 s l = s r s_l=s_r sl=sr 使得 ∀ k ∈ ( l , r ) ,    s k < s l \forall k\in(l,r),\;s_k<s_l k(l,r),sk<sl,最大化 ( r − l ) (r-l) (rl)

注意到 s i s_i si 的变化是 ± 1 \pm 1 ±1,所以其实 只需找出前缀 max ⁡ \max max 和后缀 max ⁡ \max max 。因为前缀 max ⁡ \max max 相差 1 1 1,所以二者构成的左闭右开区间必然可行,比其更小的不用再找,不存在比其更大的。

想到上面这个东西的原因是,考虑最简单的做法,离线后扫描右端点,维护左端点对应答案。你会发现是单调栈,也就是找出上一个比自己大的值,也就是后缀 max ⁡ \max max

本题中最舒服的是, max ⁡ \max max 之间完全不必考虑。所以直接在单调栈上求贡献即可。

再考虑一下发现可以在线,反正单调栈的结构是固定的树形结构。倍增。时间复杂度 O ( m log ⁡ n ) \mathcal O(m\log n) O(mlogn),空间复杂度 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn)

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
	int a = 0, f = 1, c = getchar();
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(const int &x){
	if(x > 9) writeint(x/10);
	putchar((x%10)^48);
}
inline void getMax(int &x, const int &y){
	if(x < y) x = y;
}

const int MAXN = 100005, LOGN = 18;
int lfa[MAXN][LOGN], lv[MAXN][LOGN];
int rfa[MAXN][LOGN], rv[MAXN][LOGN];
int a[MAXN], sta[MAXN], top;
char str[MAXN];
int main(){
	int n = readint(), m = readint();
	scanf("%s",str+2);
	rep(i,2,n+1){
		if(str[i] == 'F') a[i] = a[i-1]-1;
		else a[i] = a[i-1]+1; // ')'
	}
	rep(i,1,n+1){
		while(top && a[sta[top]] <= a[i]) -- top;
		lfa[i][0] = sta[top], lv[i][0] = i-sta[top]-1;
		for(int j=0; lfa[i][j]; ++j){
			lfa[i][j+1] = lfa[lfa[i][j]][j];
			lv[i][j+1] = max(lv[i][j],lv[lfa[i][j]][j]);
		}
		sta[++ top] = i;
	}
	drep(i,n+1+(top=0),1){
		while(top && a[sta[top]] <= a[i]) -- top;
		rfa[i][0] = sta[top], rv[i][0] = sta[top]-i-1;
		for(int j=0; rfa[i][j]; ++j){
			rfa[i][j+1] = rfa[rfa[i][j]][j];
			rv[i][j+1] = max(rv[i][j],rv[rfa[i][j]][j]);
		}
		sta[++ top] = i;
	}
	for(int l,r,x,y,res=0; m; --m,res=0){
		l = y = readint(), r = x = readint()+1;
		for(int j=LOGN-1; ~j; --j){
			if(lfa[x][j] && lfa[x][j] >= l-1){
				res = max(res,lv[x][j]);
				x = lfa[x][j]; // jumping
			}
			if(rfa[y][j] && rfa[y][j] <= r+1){
				res = max(res,rv[y][j]);
				y = rfa[y][j];
			}
		}
		res = std::max(res,x-y);
		writeint(res), putchar('\n');
	}
	return 0;
}
举报

相关推荐

0 条评论