考虑给出 \(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;
}
一定要多思考,不能怠惰啊。
- Permutation AtCoder Regular Contest Bracketpermutation atcoder regular contest permutation division atcoder regular atcoder regular contest 165 minimization atcoder regular contest atcoder regular contest 166 atcoder regular contest degree atcoder regular contest sliding atcoder regular contest 164 atcoder regular contest 167 atcoder regular contest builder