找平衡树练习题的时候发现了这道神题,可以说这道题是近几年单考splay的巅峰之作了。

题目大意:给出括号序列,实现区间翻转,区间反转和区间更改。查询区间最少要用几次才能改成合法序列。

分析:

  首先我们单看查询操作。不妨想象一下存在着一个栈,往里面入栈和出栈,那么从原序列中取出一对(),对应着一个入和一个出。那么当)的前面再也找不到(与之对应的时候,)就要被更改成(。

  这有什么用呢?我们会发现,删除一对()对答案不具有影响。接着我们删除所有可以匹配的括号,得到一个))))))((((((这样的序列。

  这个的答案是什么?由于对答案没有影响,我们单独考虑这个序列。我们把这个序列分成两部分考虑,对于由)构成的部分,取2的最大倍数个,其中的一半都要换成(,对于可能的多余的一个,我们一定将它变成(,接着用同样的想法考虑另一半,由于另一半一定是2的倍数个,所以答案加上另一半的一半。总的来说,答案等于ceil(“)序列长度”/2)+ceil("(序列长度"/2)

  修改操作怎么实现?假设我们有两棵括号序列正好相反的平衡树,那么区间反转操作可以视为将另一棵的这一部分嫁接过来,再把自己的这一部分嫁接过去。接着update答案即可。在草稿纸上面画一下区间翻转操作,发现翻转后的原序列与它的反序列在图形上对称。即翻转后序列的)序列等于反转后(序列长度。反之也是如此。那么这两个操作就很好解决了。

  接下来是区间更改操作,它可以覆盖掉另外两个操作,同时反转操作对它产生反转更改标记的影响。

代码:

splay容易打错,我用了treap替代它。

 #include<bits/stdc++.h>
#pragma GCC optimize(2)
#define L (t[now].ch[0])
#define R (t[now].ch[1])
using namespace std; struct node{
int data,sz,lk[],rk[];
int ch[],key;
int lzfz,lzrp,lzinv;
}t[];
int n,m,Num;
int root1;
char str[]; void push_down1(int now){
if(now == ) return;
swap(t[now].lk[],t[now].rk[]);
swap(t[now].rk[],t[now].lk[]);
swap(L,R);
t[L].lzfz ^= ; t[R].lzfz ^= ;t[now].lzfz ^= ;
} void push_down2(int now){
if(now == ) return;
swap(t[now].lk[],t[now].lk[]);
swap(t[now].rk[],t[now].rk[]);
t[L].data *= -;t[R].data *= -;
t[L].lzinv ^= ; t[R].lzinv ^= ;t[now].lzinv ^= ;
t[L].lzrp *= -; t[R].lzrp *= -;
} void push_down3(int now){
if(now == ) return;
t[L].lzfz = t[L].lzinv = ; t[R].lzfz = t[R].lzinv = ;
t[L].data = t[R].data = t[now].lzrp;
t[L].lzrp = t[now].lzrp;t[R].lzrp = t[now].lzrp;
//last buding paichu
t[L].lk[] = t[L].lk[] = t[L].rk[] = t[L].rk[] = ;
t[R].lk[] = t[R].lk[] = t[R].rk[] = t[R].rk[] = ;
//last buding paichu
if(t[now].lzrp == ){
t[L].rk[] = t[L].lk[] = t[L].sz;
t[R].rk[] = t[R].lk[] = t[R].sz;
}else{
t[L].lk[] = t[L].rk[] = t[L].sz;
t[R].lk[] = t[R].rk[] = t[R].sz;
}
t[now].lzrp = ;
} void push_up(int now){
if(t[now].lzfz) push_down1(now);
if(t[now].lzinv) push_down2(now);
if(t[now].lzrp) push_down3(now);
//rouyan chachu loudong
if(t[L].lzfz) push_down1(L);if(t[R].lzfz) push_down1(R);
if(t[L].lzinv) push_down2(L);if(t[R].lzinv) push_down2(R);
if(t[L].lzrp) push_down3(L);if(t[R].lzrp) push_down3(R); t[now].sz = t[L].sz + t[R].sz + ;
t[now].lk[] = t[L].lk[];t[now].rk[] = t[L].rk[];
t[now].lk[] = t[L].lk[];t[now].rk[] = t[L].rk[];
if(t[now].data == )t[now].rk[]++;
else if(t[now].rk[])t[now].rk[]--;else t[now].lk[]++;
if(t[now].data == -)t[now].rk[]++;
else if(t[now].rk[])t[now].rk[]--;else t[now].lk[]++;
if(t[now].rk[] >= t[R].lk[])t[now].rk[] += t[R].rk[]-t[R].lk[];
else t[now].lk[] += t[R].lk[]-t[now].rk[],t[now].rk[]=t[R].rk[];
if(t[now].rk[] >= t[R].lk[]) t[now].rk[] += t[R].rk[]-t[R].lk[];
else t[now].lk[] += t[R].lk[]-t[now].rk[],t[now].rk[]=t[R].rk[];
} int merge(int r1,int r2){
if(t[r1].lzfz) push_down1(r1); if(t[r1].lzinv) push_down2(r1);
if(t[r1].lzrp) push_down3(r1); if(t[r2].lzfz) push_down1(r2);
if(t[r2].lzinv) push_down2(r2); if(t[r2].lzrp) push_down3(r2);
if(r1 == ) return r2; if(r2 == ) return r1;
if(t[r1].key < t[r2].key){
t[r1].ch[] = merge(t[r1].ch[],r2);
push_up(r1); return r1;
}else{
t[r2].ch[] = merge(r1,t[r2].ch[]);
push_up(r2); return r2;
}
} pair <int,int> split(int now,int sz){
if(t[now].lzfz) push_down1(now);
if(t[now].lzinv) push_down2(now);
if(t[now].lzrp) push_down3(now);
if(sz == ) return make_pair(,now);
if(sz >= t[now].sz) return make_pair(now,);
pair <int,int> pi;
if(t[L].sz >= sz)
pi=split(L,sz),t[now].ch[]=pi.second,pi.second=now;
else
pi=split(R,sz-t[L].sz-),t[now].ch[]=pi.first,pi.first=now;
push_up(pi.first); push_up(pi.second);
return pi;
} void flip(int l,int r){
pair <int,int> pi = split(root1,r);
pair <int,int> pp = split(pi.first,l-);
int now = pp.second;
if(t[now].lzfz) push_down1(now);
if(t[now].lzinv) push_down2(now);
if(t[now].lzrp) push_down3(now);
t[now].lzfz ^= ;
root1 = merge(merge(pp.first,pp.second),pi.second);
} void link(int l,int r){
pair <int,int> pi = split(root1,r);
pair <int,int> pp = split(pi.first,l-);
int now = pp.second;
if(t[now].lzfz) push_down1(now);
if(t[now].lzinv) push_down2(now);
if(t[now].lzrp) push_down3(now);
t[now].lzinv ^= ;t[now].data *= -;
root1 = merge(merge(pp.first,pp.second),pi.second);
} void get_ans(int l,int r){
pair <int,int> pi = split(root1,r);
pair <int,int> pp = split(pi.first,l-);
int now = pp.second;
if(t[now].lzfz) push_down1(now);
if(t[now].lzrp) push_down2(now);
if(t[now].lzinv) push_down3(now);
int ans = ceil(t[now].lk[]/2.0)+ceil(t[now].rk[]/2.0);
printf("%d\n",ans);
root1 = merge(merge(pp.first,pp.second),pi.second);
} void insert(int &now,char what,int place){
t[++Num].data=(what=='('?:-);t[Num].sz=;t[Num].key=rand()%;
if(what == '(')t[Num].rk[] = ,t[Num].lk[] = ;
else t[Num].lk[] = ,t[Num].rk[] = ;
pair<int,int> p1 = split(now,place);
now = merge(merge(p1.first,Num),p1.second);
} void read(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
char ch = getchar();
while(ch != '(' && ch != ')') ch = getchar();
str[i] = ch;
}
for(int i=;i<=n;i++){
if(str[i] == '('){insert(root1,'(',i);}
else {insert(root1,')',i);}
}
} void work(){
for(int i=;i<=m;i++){
int cg,l,r; scanf("%d%d%d",&cg,&l,&r);
switch(cg){
case :{get_ans(l,r);break;}
case :{flip(l,r);break;}
case :{link(l,r);break;}
}
}
} int main(){
srand();
read();
work();
return ;
}

BZOJ2329 HNOI2011 括号修复 splay+贪心的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

  7. BZOJ2329 [HNOI2011]括号修复

    把左括号看做$1$,右括号看做$-1$,于是查询操作等于查询一个区间左边右边最大(最小)子段和 支持区间翻转,反转,覆盖操作...注意如果有覆盖操作,之前的操作全部作废了...于是在下传标记的时候要最 ...

  8. 2019.03.25 bzoj2329: [HNOI2011]括号修复(fhq_treap)

    传送门 题意简述: 给一个括号序列,要求支持: 区间覆盖 区间取负 区间翻转 查询把一个区间改成合法括号序列最少改几位 思路: 先考虑静态的时候如何维护答案. 显然把所有合法的都删掉之后序列长这样: ...

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

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

随机推荐

  1. 在Centos环境下安装兼容Apache2.4高版本SVN服务

    在阿里云Centos环境下,搭建PHP运行环境,PHP选择了php7版本,Mysql选择了5.7版本,Apache选择了2.4版本,在搭建SVN版本控制服务过程中出现了不兼容问题,当前环境下Apach ...

  2. CentOs环境下给PHP7.0安装fileinfo扩展

    由于项目搭建处于一个初步阶段,由于环境的不成熟出现过一系列的问题是难免的,在关于文件操作的程序中,报出一个缺少扩展的错误,已经解决~ 看一下官方给出的说明,http://php.net/manual/ ...

  3. Spring boot 整合mybatis

    第一步:创建maven项目并添加spring boot依赖: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns ...

  4. 使用tcpcopy导入线上流量进行功能和压力测试

    - 假设我们要上线一个两年内不会宕机的先进架构.在上线前,免不了单元测试,功能测试,还有使用ab,webbench等等进行压力测试. 但这些步骤非生产环境下正式用户的行为.或许你会想到灰度上线,但毕竟 ...

  5. Java线程编程中isAlive()和join()的使用详解

    一个线程如何知道另一线程已经结束?Thread类提供了回答此问题的方法. 有两种方法可以判定一个线程是否结束.第一,可以在线程中调用isAlive().这种方法由Thread定义,它的通常形式如下: ...

  6. Alibaba阿里巴巴开源软件列表

    整理和分享我大阿里的开源项目的相关网址: Git Hub上的开源软件网址: 1.https://github.com/alibaba 阿里巴巴开源技术汇总:115个软件 2.https://yq.al ...

  7. 图像处理------K-Means算法演示

    一:数学原理 K-Means算法的作者是MacQueen, 基本的数学原理很容易理解,假设有一个像素 数据集P.我们要根据值不同将它分为两个基本的数据集合Cluster1, Cluster2,使 用K ...

  8. servlet上传文件报错(三)

    1.具体报错如下 null null Exception in thread "http-apr-8686-exec-5" java.lang.OutOfMemoryError: ...

  9. JavaScript通过ID和name设置样式

    JavaScript通过ID和name设置样式 1.说明 (1)根据所提供的元素的id值,返回对该元素的引用或节点 document.getElementById("tr_th") ...

  10. 多线程下不重复读取SQL Server 表的数据

    在进行一些如发送短信.邮件的业务时,我们经常会使用一个表来存储待发送的数据,由后台多个线程不断的从表中读取待发送的数据进行发送,发送完成后再将数据转移到历史表中,这样保证待发送表的数据一般情况下不会太 ...