题面:

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+前缀和]的更多相关文章

  1. BZOJ 2329: [HNOI2011]括号修复( splay )

    把括号序列后一定是))))((((这种形式的..所以维护一个最大前缀和l, 最大后缀和r就可以了..答案就是(l+1)/2+(r+1)/2...用splay维护,O(NlogN). 其实还是挺好写的, ...

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

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

  3. BZOJ 2329/2209 [HNOI2011]括号修复 (splay)

    题目大意: 让你维护一个括号序列,支持 1.区间修改为同一种括号 2.区间内所有括号都反转 3.翻转整个区间,括号的方向不变 4.查询把某段区间变为合法的括号序列,至少需要修改多少次括号 给跪了,足足 ...

  4. BZOJ2329: [HNOI2011]括号修复(Splay)

    解题思路: Replace.Swap.Invert都可以使用Splay完美解决(只需要解决一下标记冲突就好了). 最后只需要统计左右括号冲突就好了. 相当于动态统计最大前缀合和最小后缀和. 因为支持翻 ...

  5. BZOJ2329 HNOI2011 括号修复 splay+贪心

    找平衡树练习题的时候发现了这道神题,可以说这道题是近几年单考splay的巅峰之作了. 题目大意:给出括号序列,实现区间翻转,区间反转和区间更改.查询区间最少要用几次才能改成合法序列. 分析: 首先我们 ...

  6. 【bzoj2329】[HNOI2011]括号修复 Splay

    题目描述 题解 Splay 由于有区间反转操作,因此考虑Splay. 考虑答案:缩完括号序列后剩下的一定是 $a$ 个')'+ $b$ 个'(',容易发现答案等于 $\lceil\frac a2\rc ...

  7. ●BZOJ 2329 [HNOI2011]括号修复.cpp

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2329 题解: Splay 类似 BZOJ 2329 [HNOI2011]括号修复 只是多了一 ...

  8. bzoj千题计划222:bzoj2329: [HNOI2011]括号修复(fhq treap)

    http://www.lydsy.com/JudgeOnline/problem.php?id=2329 需要改变的括号序列一定长这样 :)))((( 最少改变次数= 多余的‘)’/2 [上取整] + ...

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

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

随机推荐

  1. js 常用工具类

    /** * 存储sessionStorage */const setStore = (name, content) => { window.sessionStorage.setItem(name ...

  2. for循环和数组练习

    //公鸡2文,母鸡1文,小鸡半文,每种至少一只,100文买100只鸡有多少种可能 var ci =0; for(var g=1;g<50;g++){ for(var m=1;m<100;m ...

  3. C/C++程序基础 (八)数据结构

    非递归先序遍历 // 输出, 遍历左子树,遍历右子树 void firstOrder(Node* root) { stack<Node*> leftNodes; Node* curr = ...

  4. php同一个用户同时只能登陆一个, 后登陆者踢掉前登陆者(排他登陆)

    通常用户登陆,如果没有特别的限定, 同一个用户可以同时登陆, 今天搞了一个东西限定一个用户不能同时登陆到一个系统上, 后登陆者会把前面登陆的踢出来.(有点像QQ,同个帐号不能在多个地方同时在线, 后面 ...

  5. Vue钩子函数生命周期实例详解

    vue生命周期简介 Vue实例有一个完整的生命周期,也就是从开始创建.初始化数据.编译模板.挂载Dom.渲染→更新→渲染.卸载等一系列过程,我们称这是Vue的生命周期.通俗说就是Vue实例从创建到销毁 ...

  6. 时间转换,django的时间设置,re模块简单校验密码和手机号

    时间转换和密码,手机的re模块简单校验 import re,time def check_userinfo(request): pwd = request.POST.get("pwd&quo ...

  7. Xadmin添加用户小组件出错render() got an unexpected keyword argument 'renderer

    环境: Python 3.5.6 Django 2.1 Xadmin 原因: render函数在django2.1上有变化 解决方案: 1.在Python终端输入命令help('xadmin') 查看 ...

  8. 如何将int转换为datetime?

    $timestamp = 1210003200; $datetime = date('Y-m-d H:i:s', $timestamp); echo "该时间戳代表的时间:", $ ...

  9. Dataflow编程模型和spark streaming结合

    Dataflow编程模型和spark streaming结合 主要介绍一下Dataflow编程模型的基本思想,后面再简单比较一下Spark  streaming的编程模型 == 是什么 == 为用户提 ...

  10. Excel动画教程50例(一)

    Excel动画教程50例(一) 1.自动筛选 2.在Excel中字符替换 3.在Excel中冻结行列标题 4.在Excel中为导入外部数据 5.在Excel中行列快速转换 6.共享Excel工作簿 7 ...