专栏文章
题解:P8407 [COCI 2021/2022 #6] Superpozicija
P8407题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @minjuy7f
- 此快照首次捕获于
- 2025/12/02 03:36 3 个月前
- 此快照最后确认于
- 2025/12/02 03:36 3 个月前
我们知道一个括号序列合法,当且仅当将 当作 , 当作 时序列每个位置前缀和非负,且最后和为 。
如果 ,那么选哪个对于最后和的影响是相同的,而选较小的一个可以使更多的前缀和增加 ,显然不劣,于是选择较小的一个。 的情况是对称的。
如果 ,选择 时对 的贡献时 ,对 的贡献是 ;选择 时对 的贡献是 ,对 的贡献是 。我们想优先使最后的和尽量小,于是我们选择 ,并且保留一种操作 ,可以将 和 的贡献加上 。 的情况是对称的。
我们现在确定了每个位置的贡献,枚举 顺着往后扫。如果当前的前缀和 ,那么我们要选择一对 进行操作。不妨设 ,显然有 ,而我们选择哪一对操作对于最后和的影响都是 ,于是我们选择 最小的一对显然不劣。将 挂在 上,维护一个堆表示最小的 即可,区间加 直接打标记 处理,时间复杂度 。
Code:
CPP#include<bits/stdc++.h>
#define mem(a, v) memset(a, v, sizeof(a))
using namespace std;
const int maxn = 1e5 + 10;
int t, n;
char s[maxn << 1];
int a[maxn << 1], tag[maxn << 1], res[maxn];
vector<pair<int, int> > ad[maxn << 1];
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<> > q;
int main(){
scanf("%d", &t);
while (t--){
scanf("%d %s", &n, s + 1), res[0] = 0;
for (int i = 1; i <= n << 1; a[i] = tag[i] = 0, ad[i].clear(), i++);
for (;!q.empty(); q.pop());
for (int i = 1; i <= n; i++){
int x, y;
scanf("%d %d", &x, &y);
if (s[x] == '(' && s[y] == '('){
res[i] = x > y, a[min(x, y)] = 1;
}else if (s[x] == ')' && s[y] == ')'){
res[i] = x < y, a[max(x, y)] = -1;
}else{
const bool tmp = s[x] == '(';
x > y && (swap(x, y), 1538), a[s[x] == '(' ? y : x] = -1, res[i] = tmp, ad[x].push_back(make_pair(y, tmp ? i : -i));
}
}
for (int i = 1; i <= n << 1; i++){
a[i] += a[i - 1] + tag[i];
for (auto x: ad[i]){
q.push(x);
}
if (a[i] < 0){
if (q.empty()){
res[0] = 1;
break;
}
a[i]++, (q.top().first > i ? tag[q.top().first] : a[i])++, res[abs(q.top().second)] = q.top().second < 0, q.pop();
}
}
if (res[0] || a[n << 1]){
printf("-1\n");
}else{
for (int i = 1; i <= n; i++){
printf("%d ", res[i]);
}
putchar('\n');
}
}
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...