题解 P8227 【「Wdoi-5」建立与摧毁的结界】
题解
为了方便起见,
- 定义展开序列
B 表示将括号序列B 变为\verb!()()...()! 的形式。 - 定义堆叠序列
B 表示将括号序列B 变为\verb!(((...)))! 的形式。
考虑将一个形如
要想直接实现
设
-
对于
f(l,r) ,如果与A_{l+1} 相匹配的那个括号就是A_{r-1} ,那么就能发现,f(l,r)=g(l+1,r-1)+1 ;否则,考虑这样的情形:\texttt{(\textcolor{red}{(...)}\textcolor{cyan}{(...)}...\textcolor{orange}{(...)})} 需要分别展开
A[l+1:r-1] 当中的这些彩色区间,然后将A[l+1:r-1] 使用操作2 嵌套,再将整体使用操作1 展开。 -
对于
g(l,r) ,如果与A_{l+1} 相匹配的那个括号就是A_{r-1} ,那么就能发现,g(l,r)=g(l+1,r-1) ;否则,仍然考虑这样的情形:
需要分别展开
具体实现时,我们需要求出与一个括号相匹配的另外一个括号的位置。这一步骤可以进行预处理。具体而言,开一个栈,依次枚举。如果是左括号则直接入栈,如果是右括号则将它与栈顶的左括号匹配并将其弹出。容易发现这么做可以在
接着是考虑这两个「区间」
在此基础上,我们需要将括号序列
值得注意的是,如果可以将一个括号序列
但这不是最小方案。考虑如何操作最小。举出下面这个情形:
容易发现,如果存在某两个括号序列在
这是与刚刚的区间
因此发现,总复杂度为
参考代码
#include<bits/stdc++.h>
#define up(l,r,i) for(int i=l,END##i=r;i<=END##i;++i)
#define dn(r,l,i) for(int i=r,END##i=l;i>=END##i;--i)
using namespace std;
typedef long long i64;
const int INF =2147483647;
const int MAXN =1e6+3;
int n; char A[MAXN],B[MAXN]; int P[MAXN],Q[MAXN];
void pre(char X[],int U[]){
stack <int> S; up(1,n,i){
if(X[i]==')') U[i]=S.top(),U[U[i]]=i,S.pop();
else S.push(i);
}
}
int fun(int U[],int l,int r,bool f){ //0 推平
if(r-1==l) return 0;
if(U[r-1]==l+1){
if(!f) return fun(U,l+1,r-1,true)+1;
else return fun(U,l+1,r-1,true);
}
else{
int ret=0; for(int p=l+1;p!=r;p=U[p]+1)
ret+=fun(U,p,U[p],false);
return ret+(f?1:2);
}
}
bool L[MAXN],R[MAXN];
int clc(int l,int r){
if(r-1==l) return 0; int ret=0;
for(int p=l;p!=r+1;L[p]=1,p=P[p]+1);
for(int p=l;p!=r+1;R[p]=1,p=Q[p]+1);
for(int p=l;p!=r+1;L[p]=1,p=P[p]+1)
if(R[p]&&P[p]==Q[p] ) ret+=clc(p+1,P[p]-1);
for(int p=l;p!=r+1;p=P[p]+1)
if(P[p]!=Q[p]||!R[p]) ret+=fun(P,p,P[p],0);
for(int p=l;p!=r+1;p=Q[p]+1)
if(P[p]!=Q[p]||!L[p]) ret+=fun(Q,p,Q[p],0);
for(int p=l;p!=r+1;L[p]=0,p=P[p]+1);
for(int p=l;p!=r+1;R[p]=0,p=Q[p]+1);
return ret;
}
int main(){
scanf("%d%s%s",&n,A+1,B+1);
pre(A,P),pre(B,Q);
printf("%d\n",clc(1,n));
return 0;
}