有些人执着到,擦干|

园龄:粉丝:关注:

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

前言

好题。

思路分析

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

具体地,设序列 a 为括号序列的映射,当第 i 位为左括号时,ai=1,否则 ai=1。设 suma 的前缀和序列。

不妨把全部位置都填上左括号,然后改变其中的 n2 个左括号为右括号。

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

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

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

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

注意当 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;
}

本文作者:Kenma

本文链接:https://www.cnblogs.com/Kenma/p/18689767

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   _Kenma  阅读(3)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起