找平衡树练习题的时候发现了这道神题,可以说这道题是近几年单考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. 使用maven搭建hibernate的pom文件配置

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  2. shell脚本比较字符串相等

    昨天在定位一个bug的时候,需要些shell脚本,用到了字符串的比较,出了一个小问题,就搜索总结了一下. 第一种方法:-eq 对比字符串是否相等,我首先想到的就是 -eq,然而打印的结果不正确.各种e ...

  3. PAT 1002. A+B for Polynomials

    思路:就是两个多项式做加法–指数相同的相加即可,输出的时候按照指数递减输出,并且系数为0的项不输出. AC代码 #include <stdio.h> #include <vector ...

  4. LOJ6001 - 「网络流 24 题」太空飞行计划

    原题链接 Description 有个实验和个仪器,做实验有报酬买仪器有花费.每个实验都需要一些仪器,求最大净收益(实验报酬仪器花费),并输出一组方案. Solution 实验向所需仪器连边,实验的点 ...

  5. R语言数据框中,用0替代NA缺失值

    1.用0替代数据框中的缺失值NA 生成数据框: > m <- matrix(sample(c(NA, :), , replace = TRUE), ) > d <- as.da ...

  6. C++ string数据类型的实现

    #include <cstdlib> #include <cstring> class Mystring { public: Mystring(const char * pda ...

  7. 【前端】Vue2全家桶案例《看漫画》之一、添加四个导航页

    转载请注明出处:http://www.cnblogs.com/shamoyuu/p/vue_vux_app_1.html 项目github地址:https://github.com/shamoyuu/ ...

  8. Mongodb3.0.5副本集搭建及spring和java连接副本集配置

    这是去年写的一篇文档,最近突然发现并没有发不出来,因此现在补上,希望能对某些朋友有所帮助.因为当时记录时没有截图,因此这里看起来可能就比较单调. 一.基本环境: mongdb3.0.5数据库 spri ...

  9. Windows Subsystem for Linux (WSL)挂载移动硬盘U盘

    WSL想通过移动硬盘处理一些数据,结果进去了无法发现移动硬盘,于是搜了好久也没有一个正确的解决办法,终于找到一个,现在贡献出来与大家共享. WSL比起linux挂载硬盘简单一些.而且windows本身 ...

  10. java.sql.SQLException:Column Index out of range,0<1

    1.错误描述 java.sql.SQLException:Column Index out of range,0<1 2.错误原因 try { Class.forName("com.m ...