AtCoder Regular Contest 141 C Bracket and Permutation
考虑给出 \(S\),如何求 \(P, Q\)。先考虑 \(P\),那我们肯定想让 \(P\) 的每一项都尽量往前靠。设 \([1, k]\) 为合法括号串,\(S_{k + 1} = \texttt{)}\),那 \(P\) 的前 \(k\) 项依次为 \(1 \sim k\),第 \(k + 1\) 项不能为 \(k + 1\) 了,那找到 \(k + 1\) 之后的第一个左括号,设其位置为 \(p\),那 \(P_{k + 1} = p, P_{k + 2} = k + 1\)。一直如此直到某个时刻,前缀左括号数和右括号数相等,并且下一个字符是左括号,那就回到了一开始的情况,从这个左括号往后填。这样可以求出 \(P\),求 \(Q\) 类似地可以从后往前就变成了求 \(P\)。
考虑画出折线图,那可以发现,\(x = 0\) 上方部分可以依次填不需要改动顺序,\(x = 0\) 下方就先找到从左往右第一个之前没选过的左括号,和第一个之前没选过的右括号配对。
现在考虑原题,给出 \(P, Q\),构造 \(S\)。先暂时不考虑无解情况。发现如果 \(P_{2i - 1} < P_{2i}\),这一段一定在 \(x = 0\) 上方;如果 \(P_{2i - 1} > P_{2i}\),这一段一定在 \(x = 0\) 下方,并且可以确定 \(S_{2i - 1} = \texttt{(}, S_{2i} = \texttt{)}\)。那我们可以确定,折线在 \(x = 0\) 下方部分的 \(S\) 了。这时候我们惊喜地发现,因为求 \(Q\) 的过程是反过来的,所以可以类似地,根据 \(Q_{2i - 1} < Q_{2i}\) 确定折线在 \(x = 0\) 上方部分的 \(S\)。两个合在一起,我们发现 \(S\) 可以被唯一确定!
但是你先别急,还没做完。因为我们发现有很多繁琐的无解情况需要特判。若 \(S\) 还有没被确定的部分,就一定无解;否则可以根据 \(S\) 求出 \(P, Q\),验证是否和题目给出的 \(P, Q\) 一致即可。
时间复杂度是优秀的 \(O(n)\)。
code
// Problem: C - Bracket and Permutation
// Contest: AtCoder - AtCoder Regular Contest 141
// URL: https://atcoder.jp/contests/arc141/tasks/arc141_c
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 1000100;
int n, a[maxn], b[maxn], c[maxn];
char s[maxn];
void solve() {
scanf("%d", &n);
n <<= 1;
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= n; ++i) {
scanf("%d", &b[i]);
}
for (int i = 1; i <= n; i += 2) {
if (a[i] > a[i + 1]) {
s[a[i]] = '(';
s[a[i + 1]] = ')';
}
}
for (int i = 1; i <= n; i += 2) {
if (b[i] < b[i + 1]) {
if (s[b[i]] == ')' || s[b[i + 1]] == '(') {
puts("-1");
return;
}
s[b[i]] = '(';
s[b[i + 1]] = ')';
}
}
for (int i = 1; i <= n; ++i) {
if (!s[i]) {
puts("-1");
return;
}
}
int k = 0, lst = 1, tot = 0;
for (int i = 1; i <= n; ++i) {
k += (s[i] == '(' ? 1 : -1);
if (!k) {
if (s[lst] == '(') {
for (int j = lst; j <= i; ++j) {
c[++tot] = j;
}
} else {
vector<int> vl, vr;
for (int j = lst; j <= i; ++j) {
if (s[j] == '(') {
vl.pb(j);
} else {
vr.pb(j);
}
}
for (int j = 0; j < (i - lst + 1) / 2; ++j) {
c[++tot] = vl[j];
c[++tot] = vr[j];
}
}
lst = i + 1;
}
}
for (int i = 1; i <= n; ++i) {
if (c[i] != a[i]) {
puts("-1");
return;
}
}
k = 0;
lst = n;
tot = 0;
for (int i = n; i; --i) {
k += (s[i] == '(' ? 1 : -1);
if (!k) {
if (s[lst] == '(') {
for (int j = lst; j >= i; --j) {
c[++tot] = j;
}
} else {
vector<int> vl, vr;
for (int j = lst; j >= i; --j) {
if (s[j] == '(') {
vl.pb(j);
} else {
vr.pb(j);
}
}
for (int j = 0; j < (lst - i + 1) / 2; ++j) {
c[++tot] = vl[j];
c[++tot] = vr[j];
}
}
lst = i - 1;
}
}
for (int i = 1; i <= n; ++i) {
if (c[i] != b[i]) {
puts("-1");
return;
}
}
printf("%s\n", s + 1);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}
一定要多思考,不能怠惰啊。