Parenthesis CSU - 1809 括号匹配 ST表

题解

题目大意,给你一个括号序列,匹配规则和普通的括号匹配相同。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;	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值