题目链接

题意:有一个长度为 \(n\) 的括号序列,你需要支持以下操作:

  1. 将 \([l,r]\) 中所有括号变为 \(c\)
  2. 将 \([l,r]\) 区间翻转
  3. 将 \([l,r]\) 区间中左括号变右括号,右括号变左括号
  4. 求最少需要改变多少个括号才能使得 \([l,r]\) 变成合法括号序列,保证区间长度为偶数。

    \(1 \leq n \leq 10^5\)

基础的 fhq-treap 的题目,主要练下放标记的技巧。

首先我们需要将要求的东西转化为一个式子。例如括号序列 \((())))))((()\),将左右括号抵消掉之后就是 \())))((\),发现抵消完了之后变成了一段左括号跟一段右括号。

我们记 \((=-1\),\()=1\),假设前缀最大值为 \(mx\),后缀最小值为 \(mn\),那么最后会剩下 \(mx\) 个右括号和 \(-mn\) 个左括号,最少需要需要 \(\lceil \frac{mx}{2} \rceil+\lceil -\frac{mn}{2} \rceil\) 次操作。

我们建一棵平衡树,每个节点维护以下六个值:\(sz\) 子树大小,\(sum\) 子树权值和,\(prmn,prmx,sfmn,sfmx\) 表示前缀和后缀的最值,更新方式与最大子段和类似。

对于三个修改操作,我们考虑以下处理方式:

  1. 区间赋值,直接维护标记然后更新 \(sum\) 和 最值。
  2. 区间翻转操作,直接交换左右儿子和前、后缀最大最小值。
  3. 区间取逆操作,实际上就是将每个点的权值变为它的相反数,它的和、前后缀最大最小值也都变为了各自的相反数,如果有赋值标记,那么赋值标记也要取反。需要注意的一点是,最大值取了个相反数之后就变成了最小值,最小值取了相反数之后就变成了最大值,因此还需 swap 一下。

细节还是挺多的,代码也调了不少时间:

//Coded by tzc_wk
/*
数据不清空,爆零两行泪。
多测不读完,爆零两行泪。
边界不特判,爆零两行泪。
贪心不证明,爆零两行泪。
D P 顺序错,爆零两行泪。
大小少等号,爆零两行泪。
变量不统一,爆零两行泪。
越界不判断,爆零两行泪。
调试不注释,爆零两行泪。
溢出不 l l,爆零两行泪。
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define foreach(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define all(a) a.begin(),a.end()
#define giveup(...) return printf(__VA_ARGS__),0;
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,0x3f,sizeof(a))
#define fillsmall(a) memset(a,0xcf,sizeof(a))
#define mask(a) (1ll<<(a))
#define maskx(a,x) ((a)<<(x))
#define _bit(a,x) (((a)>>(x))&1)
#define _sz(a) ((int)(a).size())
#define filei(a) freopen(a,"r",stdin);
#define fileo(a) freopen(a,"w",stdout);
#define fileio(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
#define eprintf(...) fprintf(stderr,__VA_ARGS__)
#define put(x) putchar(x)
#define eoln put('\n')
#define space put(' ')
#define y1 y_chenxiaoyan_1
#define y0 y_chenxiaoyan_0
typedef pair<int,int> pii;
inline int read(){
int x=0,neg=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') neg=-1;
c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*neg;
}
inline void print(int x){
if(x<0){
putchar('-');
print(abs(x));
return;
}
if(x<=9) putchar(x+'0');
else{
print(x/10);
putchar(x%10+'0');
}
}
inline int qpow(int x,int e,int _MOD){
int ans=1;
while(e){
if(e&1) ans=ans*x%_MOD;
x=x*x%_MOD;
e>>=1;
}
return ans;
}
int n=read(),m=read();
char str[100005];
struct node{
int ch[2],val,key;
int prmx,prmn,sfmx,sfmn,sum,sz;
int rv_lz,cov_lz,inv_lz;
} s[100005];
int ncnt=0,root;
inline void pushup(int k){
// s[0].prmx=s[0].sfmx=0;s[0].prmn=s[0].sfmn=0x3f3f3f3f;
s[k].prmx=max(s[s[k].ch[0]].prmx,s[s[k].ch[0]].sum+s[k].val+s[s[k].ch[1]].prmx);
s[k].prmn=min(s[s[k].ch[0]].prmn,s[s[k].ch[0]].sum+s[k].val+s[s[k].ch[1]].prmn);
s[k].sfmx=max(s[s[k].ch[1]].sfmx,s[s[k].ch[1]].sum+s[k].val+s[s[k].ch[0]].sfmx);
s[k].sfmn=min(s[s[k].ch[1]].sfmn,s[s[k].ch[1]].sum+s[k].val+s[s[k].ch[0]].sfmn);
s[k].sum=s[s[k].ch[0]].sum+s[s[k].ch[1]].sum+s[k].val;
s[k].sz=s[s[k].ch[0]].sz+s[s[k].ch[1]].sz+1;
}
inline void inv_part(int k){
s[k].val=-s[k].val;s[k].sum=-s[k].sum;
int prmn=s[k].prmn,prmx=s[k].prmx,sfmn=s[k].sfmn,sfmx=s[k].sfmx;
s[k].prmn=-prmx;s[k].prmx=-prmn;s[k].sfmn=-sfmx;s[k].sfmx=-sfmn;
s[k].cov_lz=-s[k].cov_lz;
s[k].inv_lz^=1;
}
inline void cov_part(int k,int mk){
s[k].val=mk;
s[k].sum=mk*s[k].sz;
s[k].cov_lz=mk;
if(mk==1){s[k].prmn=s[k].sfmn=0;s[k].prmx=s[k].sfmx=s[k].sum;}
else{s[k].prmx=s[k].sfmx=0;s[k].prmn=s[k].sfmn=s[k].sum;}
}
inline void rev_part(int k){
swap(s[k].ch[0],s[k].ch[1]);
swap(s[k].prmn,s[k].sfmn);
swap(s[k].prmx,s[k].sfmx);
s[k].rv_lz^=1;
}
inline void pushdown(int k){
if(s[k].inv_lz){
if(s[k].ch[0]) inv_part(s[k].ch[0]);
if(s[k].ch[1]) inv_part(s[k].ch[1]);
s[k].inv_lz=0;
}
if(s[k].cov_lz){
if(s[k].ch[0]) cov_part(s[k].ch[0],s[k].cov_lz);
if(s[k].ch[1]) cov_part(s[k].ch[1],s[k].cov_lz);
s[k].cov_lz=0;
}
if(s[k].rv_lz){
if(s[k].ch[0]) rev_part(s[k].ch[0]);
if(s[k].ch[1]) rev_part(s[k].ch[1]);
s[k].rv_lz=0;
}
}
inline int newnode(char c){
ncnt++;
s[ncnt].key=rand()<<15|rand();
s[ncnt].sz=1;
if(c=='('){
s[ncnt].prmn=s[ncnt].sfmn=-1;
s[ncnt].prmx=s[ncnt].sfmx=0;
s[ncnt].sum=-1;
s[ncnt].val=-1;
}
else{
s[ncnt].prmn=s[ncnt].sfmn=0;
s[ncnt].prmx=s[ncnt].sfmx=1;
s[ncnt].sum=1;
s[ncnt].val=1;
}
return ncnt;
}
inline void build(int &k,int l,int r){
int mid=(l+r)>>1;
k=newnode(str[mid]);
if(l!=mid) build(s[k].ch[0],l,mid-1);
if(r!=mid) build(s[k].ch[1],mid+1,r);
pushup(k);
}
inline void split(int k,int sz,int &a,int &b){
if(!k){
a=b=0;
return;
}
pushdown(k);
if(sz<=s[s[k].ch[0]].sz){
b=k;
split(s[k].ch[0],sz,a,s[k].ch[0]);
}
else{
a=k;
split(s[k].ch[1],sz-s[s[k].ch[0]].sz-1,s[k].ch[1],b);
}
pushup(k);
}
inline int merge(int a,int b){
pushdown(a);pushdown(b);
if(!a||!b) return a+b;
if(s[a].key<s[b].key){
s[a].ch[1]=merge(s[a].ch[1],b);
pushup(a);return a;
}
else{
s[b].ch[0]=merge(a,s[b].ch[0]);
pushup(b);return b;
}
}
inline void rev(int l,int r){
int k1,k2,k3;
split(root,l-1,k1,k2);
split(k2,r-l+1,k2,k3);
rev_part(k2);
root=merge(merge(k1,k2),k3);
}
inline void inv(int l,int r){
int k1,k2,k3;
split(root,l-1,k1,k2);
split(k2,r-l+1,k2,k3);
inv_part(k2);
root=merge(merge(k1,k2),k3);
}
inline void cov(int l,int r,int x){
int k1,k2,k3;
split(root,l-1,k1,k2);
split(k2,r-l+1,k2,k3);
cov_part(k2,x);
root=merge(merge(k1,k2),k3);
}
inline int getf(int x){
if(x&1) return (x>>1)+1;
else return x>>1;
}
inline int query(int l,int r){
int k1,k2,k3;
split(root,l-1,k1,k2);
split(k2,r-l+1,k2,k3);
int res=getf(s[k2].prmx)+getf(abs(s[k2].sfmn));
root=merge(merge(k1,k2),k3);
return res;
}
signed main(){
cin>>str+1;
build(root,1,n);
while(m--){
char opt[10];cin>>opt+1;
if(opt[1]=='R'){
int l=read(),r=read();
char c;cin>>c;
if(c=='(') cov(l,r,-1);
else cov(l,r,1);
}
if(opt[1]=='S'){
int l=read(),r=read();
rev(l,r);
}
if(opt[1]=='I'){
int l=read(),r=read();
inv(l,r);
}
if(opt[1]=='Q'){
int l=read(),r=read();
cout<<query(l,r)<<endl;
} }
return 0;
}

洛谷 P3215 [HNOI2011]括号修复 / [JSOI2011]括号序列(fhq-treap)的更多相关文章

  1. 【BZOJ2329/2209】[HNOI2011]括号修复/[Jsoi2011]括号序列 Splay

    [BZOJ2329/2209][HNOI2011]括号修复/[Jsoi2011]括号序列 题解:我们的Splay每个节点维护如下东西:左边有多少多余的右括号,右边有多少多余的左括号,同时为了反转操作, ...

  2. [HNOI2011]括号修复 / [JSOI2011]括号序列

    传送门 Solution 一道题花费了两天的时间-- 在大佬@PinkRabbit的帮助下,终于AC了,感动-- 首先,我们考虑一个括号序列被修改成合法序列需要的次数: 我们需要修改的其实是形如... ...

  3. 洛谷P3369 【模板】普通平衡树(FHQ Treap)

    题面 传送门 题解 写了一下\(FHQ\ Treap\) //minamoto #include<bits/stdc++.h> #define R register #define inl ...

  4. 洛谷 P3214 - [HNOI2011]卡农(线性 dp)

    洛谷题面传送门 又是一道我不会的代码超短的题( 一开始想着用生成函数搞,结果怎么都搞不粗来/ll 首先不妨假设音阶之间存在顺序关系,最终答案除以 \(m!\) 即可. 本题个人认为一个比较亮的地方在于 ...

  5. BZOJ 2329: [HNOI2011]括号修复 [splay 括号]

    题目描述 一个合法的括号序列是这样定义的: 空串是合法的. 如果字符串 S 是合法的,则(S)也是合法的. 如果字符串 A 和 B 是合法的,则 AB 也是合法的. 现在给你一个长度为 N 的由‘(' ...

  6. 洛谷P3205 [HNOI2011]合唱队 DP

    原题链接点这里 今天在课上听到了这个题,听完后觉得对于一道\(DP\)题目来说,好的状态定义就意味着一切啊! 来看题: 题目描述 为了在即将到来的晚会上有更好的演出效果,作为AAA合唱队负责人的小A需 ...

  7. 洛谷P3216 [HNOI2011] 数学作业 [矩阵加速,数论]

    题目传送门 数学作业 题目描述 小 C 数学成绩优异,于是老师给小 C 留了一道非常难的数学作业题: 给定正整数 N和 M,要求计算 Concatenate (1 .. N)Mod M 的值,其中 C ...

  8. 洛谷 P3216 [HNOI2011]数学作业

    最近学了矩阵,kzj大佬推荐了我这一道题目. 乍一眼看上去,没看出是矩阵,就随便打了一个暴力,30分. 然后仔细分析了一波,发现蛮简单的. 结果全wa了,先看看下面的错误分析吧! 首先,设f[n]为最 ...

  9. 洛谷P3211 [HNOI2011]XOR和路径(期望dp+高斯消元)

    传送门 高斯消元还是一如既往的难打……板子都背不来……Kelin大佬太强啦 不知道大佬们是怎么发现可以按位考虑贡献,求出每一位是$1$的概率 然后设$f[u]$表示$u->n$的路径上这一位为$ ...

随机推荐

  1. FastAPI 学习之路(三十七)元数据和文档 URL

    你可以在 FastAPI 应用中自定义几个元数据配置. 你可以设定: Title:在 OpenAPI 和自动 API 文档用户界面中作为 API 的标题/名称使用. Description:在 Ope ...

  2. 【数据结构与算法Python版学习笔记】树——二叉树的应用:解析树

    解析树(语法树) 将树用于表示语言中句子, 可以分析句子的各种语法成分, 对句子的各种成分进行处理 语法分析树 程序设计语言的编译 词法.语法检查 从语法树生成目标代码 自然语言处理 机器翻译 语义理 ...

  3. 敏捷 Scrum Master 的難點

    什麼是 Scrum Master? Scrum master 是一個團隊角色,負責確保團隊遵守敏捷方法和原則並符合團隊的流程和實踐. Scrum Master 促進敏捷開發團隊成員之間的協作.Scru ...

  4. 使用registry搭建docker私服仓库

    使用registry搭建docker私服仓库 一.拉取 registry镜像 二.根据镜像启动一个容器 1.创建一个数据卷 2.启动容器 三.随机访问一个私服的接口,看是否可以返回数据 四.推送一个镜 ...

  5. 你知道怎么从jar包里获取一个文件的内容吗

    目录 背景 报错的代码 原先的写法 编写测试类 找原因 最终代码 背景 项目里需要获取一个excle文件,然后对其里的内容进行修改,这个文件在jar包里,怎么尝试都读取不成功,但是觉得肯定可以做到,因 ...

  6. IELTS6 2020.7 Translation

    原文 <三国演义>(The Romance of the ThreeKingdoms)是中国一部著名的历史小说,写于十四世纪.这部文学作品以三国时期的历史为背景,描写了从公元二世纪下半叶到 ...

  7. 2万字|30张图带你领略glibc内存管理精髓(因为OOM导致了上千万损失)

    前言 大家好,我是雨乐. 5年前,在上家公司的时候,因为进程OOM造成了上千万的损失,当时用了一个月的时间来分析glibc源码,最终将问题彻底解决. 最近在逛知乎的时候,发现不少人有对malloc/f ...

  8. httprunner3源码解读(2)models.py

    源码目录结构 我们首先来看下models.py的代码结构 我们可以看到这个模块中定义了12个属性和22个模型类,我们依次来看 属性源码分析 import os from enum import Enu ...

  9. Linux内核 fork 源码分析

    内核版本:linux-4.4.18 源码位置:这里 fork相关的代码最终执行的函数为_do_fork(),下面按照顺序分析下_do_fork(): 首先判断是否需要trace(跟踪)这个进程,这一步 ...

  10. GitHub上 README 增加图片标签

    hey Guys~ 你可能遇到的GitHub上好的项目都有一个非常棒的README,其中不乏用到一些非常好看的标签.比如下面这样: walle fastjson 那我们怎样自己添加一个高大上图片标签呢 ...