BZOJ2329 HNOI2011 括号修复 splay+贪心
找平衡树练习题的时候发现了这道神题,可以说这道题是近几年单考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+贪心的更多相关文章
- BZOJ2329: [HNOI2011]括号修复(Splay)
解题思路: Replace.Swap.Invert都可以使用Splay完美解决(只需要解决一下标记冲突就好了). 最后只需要统计左右括号冲突就好了. 相当于动态统计最大前缀合和最小后缀和. 因为支持翻 ...
- BZOJ 2329: [HNOI2011]括号修复( splay )
把括号序列后一定是))))((((这种形式的..所以维护一个最大前缀和l, 最大后缀和r就可以了..答案就是(l+1)/2+(r+1)/2...用splay维护,O(NlogN). 其实还是挺好写的, ...
- bzoj千题计划222:bzoj2329: [HNOI2011]括号修复(fhq treap)
http://www.lydsy.com/JudgeOnline/problem.php?id=2329 需要改变的括号序列一定长这样 :)))((( 最少改变次数= 多余的‘)’/2 [上取整] + ...
- 【bzoj2329】[HNOI2011]括号修复 Splay
题目描述 题解 Splay 由于有区间反转操作,因此考虑Splay. 考虑答案:缩完括号序列后剩下的一定是 $a$ 个')'+ $b$ 个'(',容易发现答案等于 $\lceil\frac a2\rc ...
- BZOJ 2329: [HNOI2011]括号修复 [splay 括号]
题目描述 一个合法的括号序列是这样定义的: 空串是合法的. 如果字符串 S 是合法的,则(S)也是合法的. 如果字符串 A 和 B 是合法的,则 AB 也是合法的. 现在给你一个长度为 N 的由‘(' ...
- BZOJ 2329/2209 [HNOI2011]括号修复 (splay)
题目大意: 让你维护一个括号序列,支持 1.区间修改为同一种括号 2.区间内所有括号都反转 3.翻转整个区间,括号的方向不变 4.查询把某段区间变为合法的括号序列,至少需要修改多少次括号 给跪了,足足 ...
- BZOJ2329 [HNOI2011]括号修复
把左括号看做$1$,右括号看做$-1$,于是查询操作等于查询一个区间左边右边最大(最小)子段和 支持区间翻转,反转,覆盖操作...注意如果有覆盖操作,之前的操作全部作废了...于是在下传标记的时候要最 ...
- 2019.03.25 bzoj2329: [HNOI2011]括号修复(fhq_treap)
传送门 题意简述: 给一个括号序列,要求支持: 区间覆盖 区间取负 区间翻转 查询把一个区间改成合法括号序列最少改几位 思路: 先考虑静态的时候如何维护答案. 显然把所有合法的都删掉之后序列长这样: ...
- 【BZOJ2329/2209】[HNOI2011]括号修复/[Jsoi2011]括号序列 Splay
[BZOJ2329/2209][HNOI2011]括号修复/[Jsoi2011]括号序列 题解:我们的Splay每个节点维护如下东西:左边有多少多余的右括号,右边有多少多余的左括号,同时为了反转操作, ...
随机推荐
- php中datetime时间和int时间互相转换
int时间转换datetime时间 echo date("Y-m-d H:i:s", 1210003200); datetime时间转换int时间 echo strtotime ...
- 聚类-K均值
数据来源:http://archive.ics.uci.edu/ml/datasets/seeds 15.26 14.84 0.871 5.763 3.312 2.221 5.22 Kama 14.8 ...
- PAT1078 Hashing 坑爹
思路:用筛法给素数打表,二次探测法(只需要增加的)–如果的位置被占,那么就依次探测. 注意:如果输入的,这也不是素数:如果,你需要打表的范围就更大了,因为不是素数. AC代码 #include < ...
- ubuntu常用命令操作
建立文件夹软链接 ln -s 源文件 目标文件 当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我们只要在某个固定的目录,放上该文件,然后在其它的目录下 ...
- 转 Caffe学习系列(12):训练和测试自己的图片
学习caffe的目的,不是简单的做几个练习,最终还是要用到自己的实际项目或科研中.因此,本文介绍一下,从自己的原始图片到lmdb数据,再到训练和测试模型的整个流程. 一.准备数据 有条件的同学,可以去 ...
- 网络基础Cisco路由交换四
NAT及静态转换 概述(NAT:网络地址转化) 作用: 通过将内部网络的私有ip地址翻译成全球唯一的公网ip地址, 使内部网络可以连接到互联网等外部网络上. NATA的特性 优点: 节省公有合法ip地 ...
- 【DDD】领域驱动设计实践 —— 一些问题及想法
在社区系统的DDD实践过程中,将遇到一些问题和产生的想法记录下来,共讨论. 本文为[DDD]系列文章中的其中一篇,其他内容可参考:使用领域驱动设计思想实现业务系统. 1.dto.model和entit ...
- 关于 frame的一些基本知识
关于 frame的一些基本知识只是摘抄了一部分,供初学者参考. b.帧速率: 帧速率是每秒显示的图像数.标准影片(NTSC) 是29.97 帧第秒 (fps),电影是每秒24 帧fps.欧洲标准是(P ...
- 学习笔记︱深度学习以及R中并行算法的应用(GPU)
笔记源于一次微课堂,由数据人网主办,英伟达高级工程师ParallerR原创.大牛的博客链接:http://www.parallelr.com/training/ 由于本人白痴,不能全部听懂,所以只能把 ...
- FusionCharts封装-Category
Categories.java: /** * @Title:Categories.java * @Package:com.fusionchart.model * @Description:Fusion ...