先呈上原题链接 Princess Principal

这是2018年牛客网国庆集训的题目,不会也没补,后来准备参加 秦皇岛 CCPC - Winter Camp 时在第一场热身赛时又遇到了。当时热身赛都过了一半了才想起这个事,在图书馆把签到题写了,刚看到这道题就被室友叫去打篮球了。虽然一直牵挂到这道题,直到今天才补上。。。

算是一道比较经典的括号序列题了吧。

题意:

要快速判断一个文档有没有语法错误。
有一个含有 n n n 个括号的文档。括号一共有 m m m 种,每种括号都有左括号和右括号两种形式,其中偶数为左括号,奇数为右括号,且 x 2 \lfloor \frac{x}{2} \rfloor 2x 相等时为同一种括号。如 0 <mtext>   </mtext> 2 <mtext>   </mtext> 3 <mtext>   </mtext> 1 0\space 2 \space3 \space1 0 2 3 1 为合法的括号序列,而 1 <mtext>   </mtext> 0 <mtext>   </mtext> 2 <mtext>   </mtext> 3 1 \space0 \space2 \space3 1 0 2 3 为非法的括号序列。
我们定义用如下的方式定义一个合法的文档:
1.一个空的字符串是一个合法的文档。
2.如果 A , B A,B A,B 都是合法的文档,那么 A B AB AB 也是合法的文档。
3.如果 S S S 是合法的文档,那么 a S b aSb aSb 也是合法的文档,其中 a , b a,b a,b 是同一种括号,并且 a a a 是左括号, b b b 是右括号。
现在给出 q q q 个询问,每次询问只考虑文档第 l l l r r r 个字符的情况下,文档是不是合法的。

思路:

  • 首先我们会发现,如果在某个区间里面括号 a a a 是与括号 b b b 配对的,那么无论怎么选区间,括号 a a a 始终要与括号 b b b 配对。
  • 预处理:先用正常括号匹配的思路,入栈出栈进行匹配整个括号串。用一个数组 b 记录 与其匹配的括号的位置。如 b[i]=x 表示第i个位置与第x位置是配对的。
  • 然后就转化成了 RMQ 的问题了。用线段树维护b数组区间最大最小值,如果最大值或最小值在 [ l , r ] [l,r] [l,r] 之外,那这个括号序列就不合法,反之则合法。

坑点:

good luck and have fun!!!

附上代码:

#include<bits/stdc++.h>
#define pb push_back
#define mem(a,b) memset(a,b,sizeof(a))
#define test(flag,value) cout<<flag<<":"<<(value)<<endl
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DB;
const int INF=0x3f3f3f3f;
const int MAXN=1e6;
const double PI=acos(-1);
const int MOD=1e9+7;

int minn[MAXN<<2],maxx[MAXN<<2],a[MAXN],b[MAXN];
void push_up(int rt)
{
	minn[rt]=min(minn[rt<<1],minn[rt<<1|1]);
	maxx[rt]=max(maxx[rt<<1],maxx[rt<<1|1]);
}

void build(int rt,int l,int r)
{
	if(l==r)
	{
		minn[rt]=b[l];
		maxx[rt]=b[l];
		return;
	}
	int m=(l+r)>>1;
	build(rt<<1,l,m);
	build(rt<<1|1,m+1,r);
	push_up(rt);
}

int query1(int L,int R,int l,int r,int rt)
{
	if(L<=l&&r<=R)
		return minn[rt];
	int m=(l+r)>>1;
	int x1=INF,x2=INF;
	if(m>=L) x1=query1(L,R,l,m,rt<<1);
	if(m<R) x2=query1(L,R,m+1,r,rt<<1|1);
	return min(x1,x2);
}
int query2(int L,int R,int l,int r,int rt)
{
	if(L<=l&&r<=R)
		return maxx[rt];
	int m=(l+r)>>1;
	int x1=0,x2=0;
	if(m>=L) x1=query2(L,R,l,m,rt<<1);
	if(m<R) x2=query2(L,R,m+1,r,rt<<1|1);
	return max(x1,x2);
}
stack<int> S;
stack<int> S2;
int main(void)
{
	int n,m,q;
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;i++)
		scanf("%d",a+i);
	while(!S.empty())S.pop();
	while(!S2.empty())S2.pop();
	for(int i=1;i<=n;i++)
	{
		if(S.empty())
		{
			S.push(a[i]);
			S2.push(i);
		}
		else if(S.top()==a[i]-1&&a[i]%2==1)
		{
			int x=S2.top();
			b[x]=i;
			b[i]=x;
			S.pop();S2.pop();
		}
		else
		{
			S.push(a[i]);
			S2.push(i);	
		}
	}
	while(!S2.empty())
	{
		int x=S2.top();
		b[x]=-1;
		S2.pop();
	}
	build(1,1,n);
	while(q--)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		int y1=query1(l,r,1,n,1);
		int y2=query2(l,r,1,n,1);
		if(y1<l||y2>r)
			printf("No\n");
		else
			printf("Yes\n");
	}
}