原题F - Parenthesis Checking (atcoder.jp)
题目描述
我们定义一个正确的括号序列是一个满足以下条件之一的字符串。
- 它是一个空字符串。
- 它是一串字符的连接,按照顺序是
(
、A、)
,其中A是一个正确的括号序列。 - 它是一串字符的连接,按照顺序是A、B,其中A和B都是正确的括号序列。
我们有一个长度为N的字符串S,由(
和)
组成。
给定Q个查询Query1, Query2, ..., QueryQ,按顺序处理它们。有两种类型的查询,具体格式和内容如下:
1 l r
:交换S中第l个和第r个字符。2 l r
:确定从第l个到第r个字符的连续子串是否是一个正确的括号序列。
输入描述
测试数据第一行输入两个整数N、Q
第二行输入字符串S
接下来Q行,每行一个查询。
数据范围:
- 1≤N,Q≤2×10e5
- 1≤l<r≤N
输出描述
对于格式为2 l r
的每个查询,如果连续子串是一个正确的括号序列,则打印Yes
,否则打印No
,然后换行。
测试样例
测试样例1
5 3
(())(
2 1 4
2 1 2
2 4 5
Yes
No
No
测试样例2
5 3
(())(
2 1 4
1 1 4
2 1 4
Yes
No
测试样例3
8 8
(()(()))
2 2 7
2 2 8
1 2 5
2 3 4
1 3 4
1 3 5
1 1 4
1 6 8
Yes
No
No
提示
样例一解释:
在第一个查询中,(())
是一个正确的括号序列。
在第二个查询中,((
不是一个正确的括号序列。
在第三个查询中,)(
也不是一个正确的括号序列。
思路:
这是一个线段树匹配和修改问题,由题可知,左右括号在匹配完和未匹配完前一定保证左括号数量大于 右括号数量,所以我们可以根据每个段的左右括号数量和来判断是否匹配
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5 + 10;
const ll inf = 0x3f3f3f3f3f3f;
ll n, m, ans, num;
string s;
ll arr[N];
struct node {
ll l, r, val;
ll minn;
} a[N << 2];
void build(ll node, ll l, ll r) {//建树
a[node].l = l;//每个节点的左右范围
a[node].r = r;
a[node].minn = inf;//判段该节点左右括号的和最小值,初始为最大值
if (l == r) {
a[node].val = arr[l];//左括号为1,右括号为-1
a[node].minn = arr[l];
return;
}
ll mid = (l + r) >> 1;
build(node << 1, l, mid);
build(node << 1 | 1, mid + 1, r);
a[node].val = a[node << 1].val + a[node << 1 | 1].val;
a[node].minn = min(a[node << 1].minn, a[node << 1].val + a[node << 1 | 1].minn);
}
void change(ll node, ll l, ll r, ll val) {
if (a[node].l == l && a[node].r == r) {//单点修改
a[node].val = val;
a[node].minn = val;
return;
}
ll mid = (a[node].l + a[node].r) >> 1;
if (l <= mid)change(node << 1, l, r, val);
if (r > mid)change(node << 1 | 1, l, r, val);
a[node].val = a[node << 1].val + a[node << 1 | 1].val;
a[node].minn = min(a[node << 1].minn, a[node << 1].val + a[node << 1 | 1].minn);
}
pair<ll, ll> look(ll node, ll l, ll r) {//查询
if (a[node].l >= l && a[node].r <= r) {
return {a[node].val, a[node].minn};
}
pair<ll, ll> ls, rs;
ll mid = (a[node].l + a[node].r) >> 1;
if (l <= mid)ls = look(node << 1, l, r);
if (r > mid)rs = look(node << 1 | 1, l, r);
return {ls.first + rs.first, min(ls.second, ls.first + rs.second)};
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
cin >> s;
s = " " + s;
for (ll i = 1; i <= n; i++) {
if (s[i] == '(')arr[i] = 1;
else arr[i] = -1;
}
build(1, 1, n);
while (m--) {
ll x, y, op;
cin >> op >> x >> y;
if (op == 1) {
swap(s[x], s[y]);
num = 1;
if (s[x] == ')')num = -1;
change(1, x, x, num);
num = 1;
if (s[y] == ')')num = -1;
change(1, y, y, num);
} else {
pair<ll, ll> res = look(1, x, y);
if (res.first == 0 && res.second == 0)cout << "Yes" << endl;//若res.first!=0说明左括号多了,res.second多了说明最小值小于0,即右括号多了
else cout << "No" << endl;
}
}
return 0;
}