CF1610G AmShZ Wins a Bet
feecle6418 · · 题解
蓝有一个括号串
S ,其中括号串定义为仅由(和)组成的字符串。现在她进行了任意次如下操作:
- 将
S 分为三个可以为空的连续子串A,B,C ,接着将S 变为一个新串A(B)C (即在B 前加上左括号,在B 后加上右括号)。接着,她给出了经过她任意次操作后的括号串
S ,她希望你能还原出原串S' ,使得S' 的字典序最小。对于两个字符串
a 和b ,若a 字典序比b 小,那么满足如下条件:
- 令
i 为最小的数使得a_i\neq b_i ,那么a_i 为左括号。本题数据满足:
1 \leq |S| \leq 3\times10^5 。
容易发现只要移除了 (?...):
- 如果 ? 是 (,移除 ?) 可以使得 ( 往前。
- 如果 ? 是 ),移除 (? 可以使得 ) 往后。
因此移除的一定是一些连续的合法的括号序列。
设
-
-
to_i$ 存在:$f_i=\min(S_i+f_{i+1},f_{to_i+1})
直接 dp 可以做到
如果要用数据结构优化,其实是要解决如下问题:
对于一个字符串
S ,可持久化地支持:
- 在某个版本前面添加字符。
- 比较第
i 个版本和第j 个版本的字典序大小。(其实就是求 LCP 长度)
暴力上可持久化平衡树可以做到双 log(二分 + 平衡树维护哈希)。
上面的做法大问题是,二分和平衡树的 log 是不能兼容的(因为这时两边结构不同不能平衡树上二分)。有没有什么方法不需要这么 monstrous 的数据结构?
考虑一个更显然的类似问题:
对于序列
S ,支持在前面添加数、求区间最小值。
在加入数的过程中动态维护一个 ST 表,加入一个数就
而且这个做法也可以可持久化,把 ST 表建成树上倍增数组类似物。并且,ST 表的结构决定了它可以容下在不同地方开始的二分:将其换为倍增即可。
回到原问题,套用这个做法,倍增维护哈希值即可。
具体地,开两个倍增数组,分别维护
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int B=233,mod=998244853;
string s,f[300005];
int st[300005],top,n,to[300005],p[300005][20],hsh[300005][20],pw[300005]={1},S[300005];
int main(){
cin>>s,n=s.length();
for(int i=1;i<=n;i++)pw[i]=1ll*pw[i-1]*B%mod;
for(int i=0;i<n;i++){
if(s[i]=='(')st[++top]=i;
else {
if(top)to[st[top]]=i+1,top--;
}
}
p[n][0]=S[n]=n;
for(int i=n-1;i>=0;i--){
p[i][0]=S[i+1],hsh[i][0]=s[i],S[i]=i;
for(int j=1;j<=18;j++){
p[i][j]=p[p[i][j-1]][j-1];
hsh[i][j]=(hsh[i][j-1]+1ll*hsh[p[i][j-1]][j-1]*pw[(1<<j-1)])%mod;
}
if(to[i]){
int x=i,y=S[to[i]];
for(int j=18;j>=0;j--){
if(hsh[x][j]==hsh[y][j]){
x=p[x][j],y=p[y][j];
}
}
if(y==n||(s[x]>s[y]))S[i]=S[to[i]];
}
}
int x=S[0];
while(x<n)cout<<s[x],x=p[x][0];
}