Parenthesis
Time Limit: 5 Sec Memory Limit: 128 Mb Submitted: 4132 Solved: 1026
Description
Bobo has a balanced parenthesis sequence P=p1 p2…pn of length n and q questions.
The i-th question is whether P remains balanced after pai and pbi swapped. Note that questions are individual so that they have no affect on others.
Parenthesis sequence S is balanced if and only if:
1. S is empty;
2. or there exists balanced parenthesis sequence A,B such that S=AB;
3. or there exists balanced parenthesis sequence S’ such that S=(S’).
Input
The input contains at most 30 sets. For each set:
The first line contains two integers n,q (2≤n≤105,1≤q≤105).
The second line contains n characters p1 p2…pn.
The i-th of the last q lines contains 2 integers ai,bi (1≤ai,bi≤n,ai≠bi).
Output
For each question, output “Yes” if P remains balanced, or “No” otherwise.
Sample Input
4 2
(())
1 3
2 3
2 1
()
1 2
Sample Output
No
Yes
No
Hint
Source
湖南省第十二届大学生计算机程序设计竞赛
题目大意
首先定义 平衡序列
,即:
- 空序列为平衡序列;
- 设
A
、B
为平衡序列,则(A)
是平衡序列,AB
也是平衡序列;
那么我们发现平衡序列实质上就是指每一个 )
均与左边的某个 (
唯一匹配的括号序列。
现给出一个平衡序列,再给出 q
对查询:求解将位置为 a
和 b
的括号调换之后是该序列否依然是平衡序列。
思路
如果一个序列是平衡序列,那么显然对序列任意位置都满足:从序列开始到该位置的所有 (
数量大于等于 )
数量,否则必然有 )
找不到匹配。既然如此,不妨用 前缀和
记录 (
和 )
的分布情况,即令 (
的贡献值为 1
,)
的贡献值为 -1
,这样前缀和就记录了每个位置及其左边(
比 )
多出来的数量,记该前缀和数组为 pre[]
。
接下来再来分析询问, a
和 b
的关系实质上可以分为如下几种情况:
a
和b
位置的括号相同, 即均为(
或)
, 那么显然序列的平衡性不会改变;- 若情况1不成立,则考察
a
和b
的大小关系, 进行交换使得a < b
成立; - 若
a
位置为)
而b
位置为(
,那么显然, 因为a
位置的括号往右移动,仍然可以和之前匹配的(
匹配。b
位置同理; a
位置为(
而b
位置为)
,此次交换会且仅会改变从a
位置到b - 1
位置的前缀和数组pre[]
——因为对该段上的每一个元素, 其左边(包括本身)少了一个(
而多了一个)
即前缀和要减去2
。为了使该段上的每个前缀和仍然满足pre[i] >= 0
就必须保证之前的所有pre[i]
满足pre[i] >= 2
。 由于q
的规模很大, 当然不能在线查询a
到b - 1
之间所有pre[i]
的值。由于我太菜而学不会RMQ
,也懒得使用码量巨大的线段树
来维护,所以可以使用第二个前缀和数组preLes2[]
来维护pre[i] < 2
的数量, 使用时检查preLes2[b - 1] - preLes2[a - 1] <= 0
是否成立, 若成立则代表该段上没有pre[i] < 2
的元素, 也就代表交换a
、b
后该序列依然为平衡序列;
AC代码
#include <bits/stdc++.h>
using namespace std;
inline int read()
{
char ch;
bool isNeg = false;
int a = 0;
while(!((((ch = getchar()) >= '0') && (ch <= '9')) || (ch == '-')));
if(ch != '-')
{
a *= 10;
a += ch - '0';
}
else
{
isNeg = true;
}
while(((ch = getchar()) >= '0') && (ch <= '9'))
{
a *= 10;
a += ch - '0';
}
if(isNeg)
{
a = -a;
}
return a;
}
inline void write(int a)
{
if(a < 0)
{
putchar('-');
a = -a;
}
if(a >= 10)
{
write(a / 10);
}
putchar(a % 10 + '0');
}
const int MAXN = 1e5 + 5;
inline int getVal(char c)
{
return c == '(' ? 1 : -1;
}
int main()
{
int n, q;
while (~scanf("%d %d", &n, &q))
{
static char seq[MAXN];
scanf("%s", seq + 1);
static int pre[MAXN], preLes2[MAXN];
for (int i = 1; seq[i] != '\0'; i++)
pre[i] = pre[i - 1] + getVal(seq[i]),
preLes2[i] = preLes2[i - 1] + (pre[i] < 2);
for (int i = 1; i <= q; i++)
{
int a = read(), b = read();
if (seq[a] == seq[b]) puts("Yes");
else
{
if (a > b) swap(a, b);
if (seq[a] == ')' || preLes2[b - 1] - preLes2[a - 1] <= 0)
puts("Yes");
else puts("No");
}
}
}
}
TIPS
- 序列的位置从
1
开始计数; - 询问中没有保证
a
和b
之间的大小关系;
总结
关键要找出平衡序列的本质。激励我学习 RMQ
。