找平衡树练习题的时候发现了这道神题,可以说这道题是近几年单考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. 深入js正则

    开题 我们常常有正则的各种需求,普通的正则匹配符虽然够用,但是满足不了我们一些很奇怪的需求,所以我们需要更多的关于正则的知识点. 需求 比如我们有一个这样的需求,匹配出字符串里的第一个div标签:aa ...

  2. Jenkins系列——使用checkstyle进行代码规范检查【升级版】

    1.背景 在<Jenkins系列——使用checkstyle进行代码规范检查>一文中完成了ant实现代码规范检查的例子.但存在以下缺陷: 每个作业都需要配置一个不同的checkstyle ...

  3. 机器学习策略——DeepLearning.AI课程总结

    一.什么是ML策略 假设你正在训练一个分类器,你的系统已经达到了90%准确率,但是对于你的应用程序来说还不够好,此时你有很多的想法去继续改善你的系统: 收集更多训练数据 训练集的多样性不够,收集更多的 ...

  4. 算法提高 P1001

    必须感叹下,大数模板就是好用! AC代码: #include <cstdio> #include <cmath> #include <algorithm> #inc ...

  5. 浅谈 Glide - BitmapPool 的存储时机 & 解答 ViewTarget 在同一View显示不同的图片时,总用同一个 Bitmap 引用的原因

    作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...

  6. http缓存(http caching)

    通过使用缓存web网站和web应用的性能能够得到显著的提升.Web caches能够减小延迟和网络流量,从而缩短展示资源所花费的时间. 在http中控制缓存行为的首部字段是Cache-Control, ...

  7. 面向对象的线程池Threadpool的封装

    线程池是一种多线程处理形式,预先创建好一定数量的线程,将其保存于一个容器中(如vector), 处理过程中将任务添加到队列,然后从容器中取出线程后自动启动这些任务,具体实现如下. 以下是UML图,展示 ...

  8. Pokémon Go呼应设计:让全世界玩家疯狂沉迷

    引言:什么样的呼应设计会让移动游戏玩家沉迷?那必须为玩家构建一个属于玩家本人或者被玩家认可的虚拟环境,或者说是被玩家认可的虚拟世界.在移动游戏时代,想要做到这一点并不容易.但Pokémon Go却做到 ...

  9. HI3531的DDR3配置流程

    DDR3 初始化配置流程 系统上电之后,必须先完成DDR3 SDRAM 的初始化操作,系统才能访问DDR3 SDRAM.在进行初始化之前需要注意以下几点: 对DDR3 SDRAM 进行上电操作时,需要 ...

  10. dojo中取查询出来的地市维表数据的id

    1.页面中引入隐藏域 <input type="hidden" id="area_desc"/> 2.通过在属性中设置 onchange:funct ...