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; }
It is your time to fight!