当前位置: 首页 > 工具软件 > Rope > 使用案例 >

【Rope大法好】【STL中丧心病狂的可持久化平衡树】

谷泳
2023-12-01

曾经我不会写平衡树……于是在STL中乱翻……学到了pb_ds库中的SXBK的斐波那契堆、支持kth的set,和……ext/rope

先发一个官方的 说明 (鸣谢maoxiaohan1999):

http://www.sgi.com/tech/stl/Rope.html

再来例题

IOI2012

scrivener

题意

设计支持如下 3 种操作: 
1.T x:在文章末尾打下一个小写字母 x。(type 操作) 
2.U x:撤销最后的x 次修改操作。(Undo 操作) 
(注意Query 操作并不算修改操作) 
3.Q x:询问当前文章中第x 个字母并输出。(Query 操作)

操作数n<=100000 在线算法

clj都说这是道rope傻逼题……

我的rope标程:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
const int maxn=1e5+10;
rope<char> *his[maxn];
int n;
int d[maxn];
inline int lowbit(int x){
	return x&-x;
}
inline void updata(int x){
	while(x<=n){
		d[x]++;
		x+=lowbit(x);
	}
}
inline int get(int x){
	int res=0;
	while(x){
		res+=d[x];
		x-=lowbit(x);
	}return res;
}
inline char getC(){
	char ch=getchar();
	while(!isalpha(ch))ch=getchar();
	return ch;
}
inline int getint(){
	int res=0;
	char ch,ok=0;
	while(ch=getchar()){
		if(isdigit(ch)){
			res*=10;res+=ch-'0';ok=1;
		}else if(ok)break;
	}return res;
}
void deb(rope<char> s){
	for(int i=0;i<s.length();i++)
	cout<<s[i];puts("");
}
int main(){
	freopen("type.in","r",stdin);
	freopen("type.out","w",stdout);
	n=getint();
	his[0]=new rope<char>();
	for(int i=1;i<=n;i++){
		his[i]=new rope<char>(*his[i-1]);
//		deb(*his[i]);
		char opt=getC();
		if(opt=='T'){
			char x=getC();
			his[i]->push_back(x);
			updata(i);
		}else
		if(opt=='U'){
			updata(i);
			int x=getint();
			int l=1,r=i,mid,now=get(i);
			while(l<r){
				mid=(l+r)>>1;
				if(now-get(mid)>x)
					l=mid+1;
				else
					r=mid;
			}
			his[i]=his[l-1];
			
		}else
		if(opt=='Q'){
			int x=getint()-1;
			putchar(his[i]->at(x));
			putchar('\n');	
		}
//		deb(*his[i]);
	}
	return 0;
}

可持久化在哪里呢?

his[i]=new rope<char>(*his[i-1]);

就是这一句!它可以实现O(1)的拷贝历史版本,由于rope的底层是平衡树,copy时copy根节点就行了

用它就可以轻松实现可持久化数组

其余操作不用多说

例二

AHOI2006文本编辑器editor

题意

设计数据结构支持

插入删除反转字符串

#include <cstdio>
#include <ext/rope>
#include <iostream>
#include <algorithm>
using namespace std;
using namespace __gnu_cxx;
crope a,b,tmp;
char s[10];
int now,n,len,size;
char str[2000000],rstr[2000000];
int main(){
    scanf("%d",&n);
    while(n--){
        scanf("%s",s);
        switch(s[0]){
            case 'M':{scanf("%d",&now);break;}
            case 'P':{now--;break;}
            case 'N':{now++;break;}
            case 'G':{putchar(a[now]);putchar('\n');break;}
            case 'I':{
                scanf("%d",&size);
                len=a.length();
                for(int i=0;i<size;i++){
                    do{str[i]=getchar();}
                    while(str[i]=='\n');
                    rstr[size-i-1]=str[i];
                }
                rstr[size]=str[size]='\0';
                a.insert(now,str);
                b.insert(len-now,rstr);
                break;
            }
            case 'D':{
                scanf("%d",&size);
                len=a.length();
                a.erase(now,size);
                b.erase(len-now-size,size);
                break;
            }
            case 'R':{
                scanf("%d",&size);
                len=a.length();
                tmp=a.substr(now,size);
                a=a.substr(0,now)+b.substr(len-now-size,size)+a.substr(now+size,len-now-size);
                b=b.substr(0,len-now-size)+tmp+b.substr(len-now,now);               
                break;
            }
        }       
    }
    return 0;
}
由于rope的底层实现,insert,erase,get都是logn的

就是翻转不行,不是自己手写的打不了标记啊!!

怎么办?

答:同时维护一正一反两个rope……反转即交换两个子串……Orz……

区间循环位移?简单,拆成多个子串连起来就好了……

区间a变b b变c c变d …… z变a? 呃……维护26个rope?

区间和?滚蛋,那是线段树的活

区间kth?sorry,与数值有关的操作rope一概不支持……

5555 维修数列只能自己写了……

最后的Hint: 

rope的部分简单操作

函数功能
push_back(x)在末尾添加x
insert(pos,x)在pos插入x
erase(pos,x)从pos开始删除x个
replace(pos,x)从pos开始换成x
substr(pos,x)提取pos开始x个
at(x)/[x]访问第x个元素

友情提示:cena不支持rope



 类似资料: