1. 程式人生 > >HihoCoder1677 : 翻轉字符串(Splay)(區間翻轉)

HihoCoder1677 : 翻轉字符串(Splay)(區間翻轉)

輸出 fec shu void esp 翻轉 amp return for

描述

給定一個字符串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)(區間翻轉)