找平衡树练习题的时候发现了这道神题,可以说这道题是近几年单考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. 老男孩Python全栈开发(92天全)视频教程 自学笔记09

    day9课程内容: 乌班图(ubuntu)64位系统 和 VMware 虚拟机安装(官网收费又麻烦,在网上找资源 安装vmware: vm运行(秘钥找度娘)--文件--新建虚拟机--自定义 下一步-- ...

  2. static_cast, dynamic_cast, const_cast 类型转换如何使用?

    static_cast 用法:static_cast < type-id > ( expression ) 说明:该运算符把expression转换为type-id类型,但没有运行时类型检 ...

  3. ReportView动态加载带参数的RDCL文件

    在vs里新建一个winform程序"ReportViewTest",在form1中添加一个reportView控件,from1的load事件如下: private void For ...

  4. Wireshark理解TCP乱序重组和HTTP解析渲染

    TCP数据传输过程 TCP乱序重组原理 HTTP解析渲染 TCP乱序重组 TCP具有乱序重组的功能.(1)TCP具有缓冲区(2)TCP报文具有序列号所以,对于你说的问题,一种常见的处理方式是:TCP会 ...

  5. node实现jsonp跨域

    1. 搭建node server //引入模块 var http=require("http"); var fs=require("fs");var url = ...

  6. 放大倍数超5万倍的Memcached DDoS反射攻击,怎么破?

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 作者:腾讯游戏云 背景:Memcached攻击创造DDoS攻击流量纪录 近日,利用Memcached服务器实施反射DDoS攻击的事件呈大幅上 ...

  7. 经典案例之MouseJack

    引言:在昨天的文章<无线键鼠监听与劫持>中,我们提到今天会向您介绍一个无线键鼠的监听与劫持的经典案例,<MouseJack>:MouseJack能利用无线鼠标和键盘存在的一些问 ...

  8. List转换成JSON对象报错(一)

    List转换成JSON对象 1.具体报错如下 Exception in thread "main" java.lang.NoClassDefFoundError: org/apac ...

  9. Linux显示按文件名降序文件

    Linux显示按文件名降序文件 youhaidong@youhaidong-ThinkPad-Edge-E545:~$ ls -lr 总用量 56 drwxr-xr-x 3 youhaidong yo ...

  10. web开发性能优化---代码优化篇

    1.合理使用缓存使用 提高性能最好最快的办法当然是通过缓存来改善,对于任何一个web开发者都应该善用缓存.Asp.net下的缓存机制十分强大,用好缓存机制可以让我们极大的改善web应用的性能. 1.页 ...