【乱搞】【CF1095E】 Almost Regular Bracket Sequence
Description
给定一个长度为 \(n\) 的小括号序列,求有多少个位置满足将这个位置的括号方向反过来后使得新序列是一个合法的括号序列。即在任意一个位置前缀左括号的个数不少于前缀右括号的个数,同时整个序列左右括号个数相同
Input
第一行是一个整数 \(n\) 代表序列长度
下面一行是括号序列
Output
输出一行一个数字代表的答案
Hint
\(1~\leq~n~\leq~10^6\)
Solution
考虑一个位置,如果是左括号,那么能将其变成右括号当且仅当:
整个序列左括号个数比右括号多 \(2\)
在这个位置之前,所有位置的前缀左括号个数都不少于前缀右括号个数
在这个位置和这个位置之后,在修改后所有位置的前缀左括号个数都不少于前缀右括号个数
我们将第三条转化一下,由于在修改之后左括号个数减一,右括号个数加一,于是我们可以将第三条改为
在这个位置和这个位置之后,修改前所有位置的前缀左括号个数都比前缀右括号个数至少多两个
这样一个位置能不能修改就仅与原序列有关了。
若果是右括号,也同理进行分析,将两个改为 \(-2\) 个即可。
于是我们处理一下前缀左括号个数-右括号个数。
考虑第二条限制,等价于 \([1,pos]\) 这些前缀中的最小值不小于 \(0\)。于是我们对这些前缀求一个前缀最小值,就可以做到 \(O(1)\) 判断了。
对于第三条限制,等价于 \([pos,n]\) 这些前缀中的最小值不小于 \(2\)。于是我们对这些前缀求一个后缀最小值,也可以做到 \(O(1)\) 判断了。
这个位置是右括号的情况同理。细节参考代码
Code
#include <cstdio>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif
#define rg register
#define ci const int
#define cl const long long
typedef long long int ll;
namespace IPT {
const int L = 1000000;
char buf[L], *front=buf, *end=buf;
char GetChar() {
if (front == end) {
end = buf + fread(front = buf, 1, L, stdin);
if (front == end) return -1;
}
return *(front++);
}
}
template <typename T>
inline void qr(T &x) {
rg char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
if (lst == '-') x = -x;
}
template <typename T>
inline void ReadDb(T &x) {
rg char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar();
if (ch == '.') {
ch = IPT::GetChar();
double base = 1;
while ((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar();
}
if (lst == '-') x = -x;
}
namespace OPT {
char buf[120];
}
template <typename T>
inline void qw(T x, const char aft, const bool pt) {
if (x < 0) {x = -x, putchar('-');}
rg int top=0;
do {OPT::buf[++top] = x % 10 + '0';} while (x /= 10);
while (top) putchar(OPT::buf[top--]);
if (pt) putchar(aft);
}
const int maxn = 1000010;
int n, ans;
int cnt[maxn], pre[maxn], post[maxn];
char MU[maxn];
int main() {
freopen("1.in", "r", stdin);
qr(n);
for (rg int i = 1; i <= n; ++i) {
do {MU[i] = IPT::GetChar();} while ((MU[i] != '(') && (MU[i] != ')'));
if (MU[i] == '(') cnt[i] = cnt[i - 1] + 1;
else cnt[i] = cnt[i - 1] - 1;
}
if ((cnt[n] != 2) && (cnt[n] != -2)) return puts("0") & 1;
pre[0] = maxn; post[n + 1] = maxn;
for (rg int i = 1; i <= n; ++i) pre[i] = std::min(pre[i - 1], cnt[i]);
for (rg int i = n; i; --i) post[i] = std::min(post[i + 1], cnt[i]);
for (rg int i = 1; i <= n; ++i) {
if (MU[i] == '(') {
if ((cnt[n] == 2) && (post[i] >= 2) && (pre[i - 1] >= 0)) ++ans;
} else {
if ((cnt[n] == -2) && (post[i] >= -2) && (pre[i - 1] >= 0)) ++ans;
}
}
qw(ans, '\n', true);
return 0;
}