HihoCoder1677 : 翻转字符串(Splay)(区间翻转)

Posted ---学习ing---

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HihoCoder1677 : 翻转字符串(Splay)(区间翻转)相关的知识,希望对你有一定的参考价值。

 

描述

给定一个字符串S,小Hi希望对S进行K次翻转操作。  

每次翻转小Hi会指定两个整数Li和Ri,表示要将S[Li..Ri]进行翻转。(S下标从0开始,即S[0]是第一个字母)  

例如对于S="abcdef",翻转S[2..3] 得到S="abdcef";再翻转S[0..5]得到S="fecdba"。

输入

第一行包含一个由小写字母组成的字符串S。  

第二行包含一个整数K。  

以下K行每行包含两个整数Li和Ri。  

对于50%的数据,1 ≤ |S| ≤ 1000  

对于100%的数据,1 ≤ |S| ≤ 100000, 1 ≤ K ≤ 100000, 0 ≤ Li ≤ Ri < |S|

输出

输出经过K次翻转后的字符串S

样例输入

abcdef  
2  
2 3  
0 5

样例输出

fecdba

比赛的时候以为可以用lazy下压标记,但是后面lazy失去了作用。这道题不能用线段树做的原因不是因为数据范围,而是这道题涉及到了翻转操作,线段树不支持这种操作,所以用splay来维护。

现在模板初步成型。

对于初始值: 

  •     如果是插入和查询一起操作,那就按顺序来。
  •     否则,初始时,二分建树。不然一个一个加,会成一条链,然后N^2爆炸。

对于区间:

  •    可能是有id的,对id在[L,R]之间进行操作,则查找L的前一个点,和R的后一个点,然后操作。(即find函数)
  •    也可能没有id,对当前队伍的从前往后的第[L,R]th之间进行操作,则先查找第L大...。(即findkth函数)
  •    如果存在反转rev操作,也只能用查找第k大来得到区间(上面第二种)。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int maxn=100010;
const int inf=1000000000;
char c[maxn];int n;
struct SplayTree
{
    int ch[maxn][2],fa[maxn],rev[maxn],sz[maxn];
    int cnt,root;
    void init()
    {
        cnt=0; root=(1+n+2)>>1; 
    }
    void build(int L,int R,int pre)
    {
        if(L>R) return ;//build不同于线段树,这里有可能大于。 
        if(L==R) {
            fa[L]=pre;sz[L]=1;
            ch[pre][L>=pre]=L;
            return ;
        }int Mid=(L+R)>>1;
        build(L,Mid-1,Mid);build(Mid+1,R,Mid);//这里好像有点乱 
        fa[Mid]=pre; pushup(Mid); 
        ch[pre][Mid>=pre]=Mid;
    }
    void pushup(int x)
    {
        sz[x]=1;
        if(ch[x][0]) sz[x]+=sz[ch[x][0]];
        if(ch[x][1]) sz[x]+=sz[ch[x][1]];
    }
    void pushdown(int x)
    {
        if(!rev[x]) return ;
        swap(ch[x][0],ch[x][1]);
        rev[ch[x][0]]^=1;rev[ch[x][1]]^=1;
        rev[x]=0;
    }
    int find(int x,int rk)//ok
    {
        pushdown(x);//莫忘 
        if(sz[ch[x][0]]==rk-1) return x;
        if(sz[ch[x][0]]>=rk) return (find(ch[x][0],rk));
        return find(ch[x][1],rk-sz[ch[x][0]]-1);
    }
    void rotate(int x,int opt)//ok
    {
        int y=fa[x],z=fa[y];
        pushdown(y);pushdown(x);
        if(z!=0) ch[z][ch[z][1]==y]=x;fa[x]=z;
        ch[y][opt]=ch[x][opt^1];fa[ch[x][opt^1]]=y;
        ch[x][opt^1]=y;fa[y]=x;
        pushup(y);
    }
    void splay(int x,int y)//把x移到y下面 
    {
        pushdown(x); 
        while(fa[x]!=y){
            int f=fa[x],ff=fa[f];
            int c1=(ch[f][1]==x),c2=(ch[ff][1]==f);//记录c1 c2,因为rotate之后儿子关系会改变。 
            if(ff==y) rotate(x,c1);
            else {
                if(c1^c2) rotate(x,c1),rotate(x,c2);
                else rotate(f,c2),rotate(x,c1);
            }
        } pushup(x); if(!y) root=x;//提到root 
    }
    void revers(int L,int R)
    {
        int x=find(root,L-1),y=find(root,R+1);
        splay(x,0); splay(y,x);
        rev[ch[y][0]]^=1;
    }
    void print(int x)
    {
        pushdown(x);
        if(ch[x][0]) print(ch[x][0]);
        if(x>=2&&x<=n+1) printf("%c",c[x]); 
        if(ch[x][1]) print(ch[x][1]);
    }
}Tree;
int main()
{
    int i,m,L,R;scanf("%s",c+2); 
    n=strlen(c+2);Tree.init();//莫忘了初始root 
    Tree.build(1,n+2,0); scanf("%d",&m);
    for(i=1;i<=m;i++){
        scanf("%d%d",&L,&R);
        Tree.revers(L+2,R+2);
    } Tree.print(Tree.root);
    return 0;
}

 

以上是关于HihoCoder1677 : 翻转字符串(Splay)(区间翻转)的主要内容,如果未能解决你的问题,请参考以下文章

hihoCoder挑战赛28题目2 : 二进制翻转

hihoCoder编程练习赛70

解决字符集不同引起的主从同步异常1677报错问题

mysql之数据库主从复制配置报错1677

hihoCoder 1039 : 字符消除

hihoCoder #1320 : 压缩字符串 区间dp