[HNOI2011][bzoj 2329] 括号修复 [splay+前缀和]
题面:
http://www.lydsy.com/JudgeOnline/problem.php?id=2329
思路:
显然,操作4中输出补全的最小费用是关键
这决定了我们不可能在splay上只维护1-2个值。
考虑一段括号序列,将其中所有合法序列删去以后,留下的一定是形如 ))))))((( 的序列
因此首先考虑将每段区间左侧不匹配的括号数和右侧不匹配的括号数记录下来,分别为 left[l,r] 和 right[l,r]
此时除了Invert操作以外已经可以满足
但是对于Invert操作,对于每一个括号取反,显然每边只记录一个是不够的。
考虑再次转化模型,将括号序列抽象化成数字的和
发现:一个匹配的括号序列中左括号等于右括号,一段缩过的序列(去掉了所有的合法序列)的左侧右括号数量和右侧左括号数量,恰等于左右括号数量的前缀和以及后缀和。
因此,将右括号 )视为-1,左括号( 视为+1,对每一段区间,记录其最小前缀和和最大后缀和,即为上文所述 left && right
同时,我们记录区间的最大前缀和和最小后缀和,作为Swap和Invert操作时候用。
对于Swap操作,即为整个序列翻转,那么其最大前缀和与最大后缀和交换,最小前缀和与最小后缀和交换
对于Invert操作,需要交换最大最小前缀和,以及最大最小后缀和,再将它们全部取反
因为每个括号取反以后,原来最小的前缀和对应的哪一个序列,现在具有所有前缀里面的最大值(在相反数意义下)
后缀同理
统计答案即为对于所求区间,求 (l1/2)+(r2/2) ,若l1,r2是奇数,则还需加二(额外费用)
综上所述,令每一个)为-1,每一个(为1,记录每一个区间的最小最大前缀和 l1,l2 以及最小最大后缀和 r1,r2,将整个序列放到splay上操作即可。
需要注意的是,更新区间全部刷成同一个值的lazy标记时,一定要同时去掉已有的invert标记,以防WA
Code:
/**************************************************************
Problem: 2329
User: dedicatus545
Language: C++
Result: Accepted
Time:10484 ms
Memory:6860 kb
****************************************************************/ #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
int re=,flag=;char ch=getchar();
while(ch>''||ch<''){
if(ch=='-') flag=-;
ch=getchar();
}
while(ch>=''&&ch<='') re=(re<<)+(re<<)+ch-'',ch=getchar();
return re*flag;
}
int n,m,root,cnt;
int fa[],ch[][],siz[];
int w[],l1[],l2[],r1[],r2[],sum[];
int rev[]={},lazy[]={},inv[]={};
int x[];
//lazy==1: (
//lazy==-1: )
void _swap(int &x,int &y){x^=y;y^=x;x^=y;}
int _max(int x,int y){return (x<y)?y:x;}
int _min(int x,int y){return (x>y)?y:x;}
void update(int x){
if(!x) return;
sum[x]=sum[ch[x][]]+sum[ch[x][]]+w[x];
siz[x]=siz[ch[x][]]+siz[ch[x][]]+;
l1[x]=_min(l1[ch[x][]],sum[ch[x][]]+w[x]+l1[ch[x][]]);
l2[x]=_max(l2[ch[x][]],sum[ch[x][]]+w[x]+l2[ch[x][]]);
r1[x]=_min(r1[ch[x][]],sum[ch[x][]]+w[x]+r1[ch[x][]]);
r2[x]=_max(r2[ch[x][]],sum[ch[x][]]+w[x]+r2[ch[x][]]);
}
void pushdown(int x,int t){
if(!x) return;
inv[x]=;w[x]=t;lazy[x]=t;
if(~t){
sum[x]=siz[x];
l1[x]=r1[x]=;
l2[x]=r2[x]=sum[x];
}
else{
sum[x]=~siz[x]+;
l2[x]=r2[x]=;
l1[x]=r1[x]=sum[x];
}
}
void pushrev(int x){
if(!x) return;
_swap(ch[x][],ch[x][]);
_swap(l1[x],r1[x]);
_swap(l2[x],r2[x]);
rev[x]^=;
}
void pushinv(int x){
if(!x) return;
_swap(l1[x],l2[x]);
_swap(r1[x],r2[x]);
l1[x]=~l1[x]+;l2[x]=~l2[x]+;
r1[x]=~r1[x]+;r2[x]=~r2[x]+;
w[x]=~w[x]+;sum[x]=~sum[x]+;inv[x]^=;
}
void push(int x){
if(!x) return;
if(rev[x]){
pushrev(ch[x][]);
pushrev(ch[x][]);
rev[x]=;
}
if(lazy[x]){
pushdown(ch[x][],lazy[x]);
pushdown(ch[x][],lazy[x]);
lazy[x]=;
}
if(inv[x]){
pushinv(ch[x][]);
pushinv(ch[x][]);
inv[x]=;
}
}
int get(int x){return ch[fa[x]][]==x;}
void rotate(int x){
int f=fa[x],ff=fa[f],son=get(x);
push(f);push(x);
ch[f][son]=ch[x][son^];
if(ch[f][son]) fa[ch[f][son]]=f;
fa[f]=x;ch[x][son^]=f;
fa[x]=ff;
if(ff) ch[ff][ch[ff][]==f]=x;
update(f);update(x);
}
void splay(int x,int to){
if(x==to||fa[x]==to) return;
if(!to) root=x;
for(int f;(f=fa[x])&&f!=to;rotate(x))
if(fa[f]!=to)
rotate(get(f)==get(x)?f:x);
update(x);
}
int rank(int x,int pos){
push(pos);
if(siz[ch[pos][]]+==x){
splay(pos,);return pos;
}
if(siz[ch[pos][]]>=x) return rank(x,ch[pos][]);
else return rank(x-siz[ch[pos][]]-,ch[pos][]);
}
int build(int le,int ri,int f){
if(le>ri) return ;
int mid=(le+ri)>>,cur=++cnt;
//cout<<"build "<<le<<" "<<ri<<" "<<mid<<" "<<x[mid]<<"\n";
w[cur]=x[mid];fa[cur]=f;
ch[cur][]=build(le,mid-,cur);
ch[cur][]=build(mid+,ri,cur);
update(cur);return cur;
}
void change(int le,int ri,int t){
int x=rank(le,root),y=rank(ri+,root);
splay(x,);splay(y,root);
pushdown(ch[y][],t);
update(y);update(x);
}
void reverse(int le,int ri){
int x=rank(le,root),y=rank(ri+,root);
splay(x,);splay(y,root);
pushrev(ch[y][]);
update(y);update(x);
}
void invert(int le,int ri){
int x=rank(le,root),y=rank(ri+,root);
splay(x,);splay(y,root);
pushinv(ch[y][]);
update(y);update(x);
}
int query(int le,int ri){
int x=rank(le,root),y=rank(ri+,root);
splay(x,);splay(y,root);
return ((r2[ch[y][]]+)>>)-((l1[ch[y][]]-)/);
}
void dfs(int u){
if(!u) return;
push(u);
dfs(ch[u][]);
printf("%d %d %d %d\n",u,fa[u],ch[u][],ch[u][]);
printf("%d %d %d %d %d\n",w[u],l1[u],l2[u],r1[u],r2[u]);
dfs(ch[u][]);
}
char s[];
int main(){
// freopen("brackets.in","r",stdin);
// freopen("brackets.out","w",stdout);
int i,t1,t2,t4;char t3;
n=read();m=read();
scanf("%s",s);
for(i=;i<=n;i++) x[i]=((s[i-]=='(')?:-);
root=build(,n+,);
//dfs(root);printf("\n");
for(i=;i<=m;i++){
scanf("%s",s);
if(s[]=='R'){
t1=read();t2=read();t3=getchar();
while(t3!='('&&t3!=')') t3=getchar();
t4=((t3=='(')?:-);
change(t1,t2,t4);
}
if(s[]=='I'){
t1=read();t2=read();
invert(t1,t2);
}
if(s[]=='Q'){
t1=read();t2=read();
printf("%d\n",query(t1,t2));
}
if(s[]=='S'){
t1=read();t2=read();
reverse(t1,t2);
}
//dfs(root);printf("\n");
}
}
[HNOI2011][bzoj 2329] 括号修复 [splay+前缀和]的更多相关文章
- BZOJ 2329: [HNOI2011]括号修复( splay )
把括号序列后一定是))))((((这种形式的..所以维护一个最大前缀和l, 最大后缀和r就可以了..答案就是(l+1)/2+(r+1)/2...用splay维护,O(NlogN). 其实还是挺好写的, ...
- BZOJ 2329: [HNOI2011]括号修复 [splay 括号]
题目描述 一个合法的括号序列是这样定义的: 空串是合法的. 如果字符串 S 是合法的,则(S)也是合法的. 如果字符串 A 和 B 是合法的,则 AB 也是合法的. 现在给你一个长度为 N 的由‘(' ...
- BZOJ 2329/2209 [HNOI2011]括号修复 (splay)
题目大意: 让你维护一个括号序列,支持 1.区间修改为同一种括号 2.区间内所有括号都反转 3.翻转整个区间,括号的方向不变 4.查询把某段区间变为合法的括号序列,至少需要修改多少次括号 给跪了,足足 ...
- BZOJ2329: [HNOI2011]括号修复(Splay)
解题思路: Replace.Swap.Invert都可以使用Splay完美解决(只需要解决一下标记冲突就好了). 最后只需要统计左右括号冲突就好了. 相当于动态统计最大前缀合和最小后缀和. 因为支持翻 ...
- BZOJ2329 HNOI2011 括号修复 splay+贪心
找平衡树练习题的时候发现了这道神题,可以说这道题是近几年单考splay的巅峰之作了. 题目大意:给出括号序列,实现区间翻转,区间反转和区间更改.查询区间最少要用几次才能改成合法序列. 分析: 首先我们 ...
- 【bzoj2329】[HNOI2011]括号修复 Splay
题目描述 题解 Splay 由于有区间反转操作,因此考虑Splay. 考虑答案:缩完括号序列后剩下的一定是 $a$ 个')'+ $b$ 个'(',容易发现答案等于 $\lceil\frac a2\rc ...
- ●BZOJ 2329 [HNOI2011]括号修复.cpp
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2329 题解: Splay 类似 BZOJ 2329 [HNOI2011]括号修复 只是多了一 ...
- bzoj千题计划222:bzoj2329: [HNOI2011]括号修复(fhq treap)
http://www.lydsy.com/JudgeOnline/problem.php?id=2329 需要改变的括号序列一定长这样 :)))((( 最少改变次数= 多余的‘)’/2 [上取整] + ...
- 【BZOJ2329/2209】[HNOI2011]括号修复/[Jsoi2011]括号序列 Splay
[BZOJ2329/2209][HNOI2011]括号修复/[Jsoi2011]括号序列 题解:我们的Splay每个节点维护如下东西:左边有多少多余的右括号,右边有多少多余的左括号,同时为了反转操作, ...
随机推荐
- python_27_多级字典嵌套及操作
#key-value 字典无下标 所以乱序,key值尽量不要取中文 person_log={ '大二':{ 'Ya Nan':['free','cute','soso'], 'Sha sha':['微 ...
- python读取图像
from PIL import Imageimg = Image.open('/Users/NaCl/Desktop/test.png')img.show()
- pandas 常用统计方法
统计方法 pandas 对象有一些统计方法.它们大部分都属于约简和汇总统计,用于从 Series 中提取单个值,或从 DataFrame 的行或列中提取一个 Series. 比如 DataFrame. ...
- C# StreamWriter对像
用FileWriter来随机读取文件是个好主意,而用StreamWriter可以直接把字符串写入文件中,它处理重要的转换和向FileStream对像写入工作.创建StreamWriter有很多方法: ...
- Mybatis自查询递归查找子
先看一下数据库 主键id,名称product_code,父parent,和kind 设计菜单类 setter,getter Dao public interface ProductMapper { L ...
- centos下修改docker连接docker_host默认方式为tls方式
1.安装docker,请参考官网文档 centos下安装docker 2.安装完成应该可以使用docker的各种命令连接docker host.docker host运行在本机上,但与localhos ...
- 第三章JavaScript 内置对象
1 Number 1.1 属性 MAX_VALUE JS可以表示的最大的数字 MIN_VALUE JS可以表示的最小的数字 1.2 方法 toFixed(length) 指定保留长度的小数 toExp ...
- css3 横向拖拽
css: .tab{ list-style-type: none; display:-webkit-box; display:-webkit-flex; ...
- vue 中有时候是数据没有同步的问题
1,在项目中,在做表格的数据渲染的时候,表格中有input标签的数据来进行双向绑定, this.$set(this.tableTitle.money, index, money[index]+isMo ...
- Node.js 特点
1.单线程 在Java.PHP或者.net等服务器端语言中,会为每一个客户端连接创建一个新的线程.而每个线程需要耗费大约2MB内存.也就是说,理论上,一个8GB内存的服务器可以同时连接的最大用户数 ...