题解
题目大意,给你一个括号序列,匹配规则和普通的括号匹配相同。q次询问每次询问独立,选择两个位置进行交换,问交换后是否括号匹配。
把左括号看做1右括号看做-1做前缀和s,对于区间[l, r]如果括号匹配则区间最小值大于等于s[l - 1],使用ST表可以log时间内得到区间最值然后判断某个区间是否括号匹配。
对于每次操作如果两个位置括号相同则不发生变化直接查询整个区间最小值。
如果左操作位置是右括号交换并做前缀和后,整个区间[l, r - 1]最小值会减少2。也不用真的去替换再做前缀和,只需要分三部分查询[1, l - 1]、[l, r - 1],[r, n]。对于[1, r - 1]查询的最值-2即可。
对于左操作位置是左括号同理,将[1, r - 1] + 2即可。对于每次询问还要判断s[n]是否是否为0表示没有多余的左括号。
AC代码
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e5 + 10;
char s[N];
int lg2[N], st[30][N];
int a[N];
void ST(int n)
{
lg2[0] = -1;
for (int i = 1; i < N; ++i)
lg2[i] = lg2[i >> 1] + 1;
for (int i = 1; i <= n; ++i)
st[0][i] = a[i];
for (int i = 1; i <= lg2[n]; ++i)
for (int j = 1; j + (1 << i) - 1 <= n; ++j)
st[i][j] = min(st[i - 1][j], st[i - 1][j + (1 << i - 1)]);
}
int Ask(int l, int r)
{
int w = lg2[r - l + 1];
return min(st[w][l], st[w][r - (1 << w) + 1]);
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
int n, q;
while (cin >> n >> q)
{
scanf("%s", s + 1);
for (int i = 1; i <= n; ++i)
{
//cout << s[i] << endl;
a[i] = a[i - 1] + (1 - (s[i] == ')') * 2);
}
ST(n);
for (int i = 0; i < q; ++i)
{
int l, r, k = INF;
scanf("%d%d", &l, &r);
if (l > r)
swap(l, r);
if (s[l] == s[r])
k = Ask(1, n);
else if (s[l] == '(')
{
if (l > 1)
k = Ask(1, l - 1);
k = min(k, Ask(l, r - 1) - 2);
k = min(k, Ask(r, n));
}
else
{
if (l > 1)
k = Ask(1, l - 1);
k = min(k, Ask(l, r - 1) + 2);
k = min(k, Ask(r, n));
}
if (a[n] == 0 && k >= 0)
printf("YES\n");
else
printf("NO\n");
}
}
return 0;
}