题解:P9406 [POI 2020/2021 R3] Nawiasowania

前言

好题。

思路分析

首先套路地把括号匹配转化为序列问题。

具体地,设序列 \(a\) 为括号序列的映射,当第 \(i\) 位为左括号时,\(a_i=1\),否则 \(a_i=-1\)。设 \(sum\)\(a\) 的前缀和序列。

不妨把全部位置都填上左括号,然后改变其中的 \(\frac{n}{2}\) 个左括号为右括号。

对于 \(sum\) 序列来说,在 \(i\) 位置将一个左括号改变为右括号相当于在 \([i,n]\) 区间减 \(2\)

因为初始 \(sum\) 序列是递增的,并且做的还是后缀减操作,所以在越靠后的位置进行操作,结果一定不劣于在靠前的位置进行操作。

所以我们可以从后往前枚举其中一个括号序列,然后判断每一个位置是不是可以在两个括号序列上同时进行操作。

用线段树维护区间加,查询区间最小值,可以做到 \(O(n\log n)\)

注意当 \(n\) 是奇数时,无解。

代码实现

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int n,p[1000005],ans[1000005],cnt;
int val[2000005][3],ls[2000005],rs[2000005],tag[2000005][3],dcnt,rt;
void pushup(int x){
	val[x][1]=min(val[ls[x]][1],val[rs[x]][1]);
	val[x][2]=min(val[ls[x]][2],val[rs[x]][2]);
}
void pushdown(int x){
	if(tag[x][1]){
		tag[ls[x]][1]+=tag[x][1];
		tag[rs[x]][1]+=tag[x][1];
		val[ls[x]][1]+=tag[x][1];
		val[rs[x]][1]+=tag[x][1];
		tag[x][1]=0;
	}
	if(tag[x][2]){
		tag[ls[x]][2]+=tag[x][2];
		tag[rs[x]][2]+=tag[x][2];
		val[ls[x]][2]+=tag[x][2];
		val[rs[x]][2]+=tag[x][2];
		tag[x][2]=0;
	}
}
void build(int l,int r,int &x){
	x=++dcnt;
	if(l==r){
		val[x][1]=val[x][2]=l/2;
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,ls[x]);
	build(mid+1,r,rs[x]);
	pushup(x);
}
void modify(int l,int r,int ql,int qr,int k,int op,int x){
	if(ql<=l && r<=qr){
		val[x][op]+=k;
		tag[x][op]+=k;
		return;
	}
	pushdown(x);
	int mid=(l+r)>>1;
	if(ql<=mid) modify(l,mid,ql,qr,k,op,ls[x]);
	if(qr>=mid+1) modify(mid+1,r,ql,qr,k,op,rs[x]);
	pushup(x);
}
int query(int l,int r,int ql,int qr,int op,int x){
	if(ql<=l && r<=qr) return val[x][op];
	pushdown(x);
	int mid=(l+r)>>1,ans=inf;
	if(ql<=mid) ans=min(ans,query(l,mid,ql,qr,op,ls[x]));
	if(qr>=mid+1) ans=min(ans,query(mid+1,r,ql,qr,op,rs[x]));
	return ans;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	if(n&1){
		cout<<"NIE";
		return 0;
	}
	for(int i=1;i<=n;i++){
		cin>>p[i];
	}
	build(1,n,rt);
	for(int i=n;i>=1;i--){
		if(query(1,n,p[i],n,2,rt)>0 && query(1,n,i,n,1,rt)>0){
			modify(1,n,p[i],n,-1,2,rt);
			modify(1,n,i,n,-1,1,rt);
			cnt++;
			ans[p[i]]=1;
		}
		if(cnt==n/2) break;
	}
	if(cnt<n/2){
		cout<<"NIE";
		return 0;
	}
	for(int i=1;i<=n;i++){
		if(ans[i]) cout<<')';
		else cout<<'(';
	}
	return 0;
}
posted @ 2025-01-24 16:42  _Kenma  阅读(41)  评论(0)    收藏  举报