簡單用法:
#include <ext/rope> using namespace __gnu_cxx; int a[1000]; rope<int> x; rope<int> x(a,a + n); rope<int> a(x); x->at(10); x[10]; x->push_back(x) // 在末尾添加x x->insert(pos,x) // 在pos插入x x->erase(pos,x) // 從pos開始刪除x個 x->replace(pos,x) // 從pos開始換成x x->substr(pos,x) // 提取pos開始x個
例題一:
IOI2012
scrivener
題意
設計支持如下 3 種操作:
1.T x:在文章末尾打下一個小寫字母 x。(type 操作)
2.U x:撤銷最后的x 次修改操作。(Undo 操作)
(注意Query 操作並不算修改操作)
3.Q x:詢問當前文章中第x 個字母並輸出。(Query 操作)
操作數n<=100000 在線算法
clj都說這是道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)的copy歷史版本,因為rope的底層是紅黑樹,所以copy時只需copy根指針。
一鍵持久化……
例題二:
BZOJ 3673: 可持久化並查集 by zky
n個集合 m個操作
操作:
1 a b 合並a,b所在集合
2 k 回到第k次操作之后的狀態(查詢算作操作)
3 a b 詢問a,b是否屬於同一集合,是則輸出1否則輸出0
0<n,m<=2*10^4
分析:直接可持久化並查集的fa數組就可以了。
ACCode:
#include <cstdio> #include <ext/rope> using namespace std; using namespace __gnu_cxx; const int maxm = 20010; rope<int> *rp[maxm]; int find(int i,int x) { if(rp[i]->at(x) == x) return x; int f = find(i,rp[i]->at(x)); if(f == rp[i]->at(x)) return f; rp[i]->replace(x,f); return rp[i]->at(x); } inline void merge(int i,int x,int y) { x = find(i,x),y = find(i,y); if(x != y) rp[i]->replace(y,x); } int a[maxm],lastans; int main() { int n,m; scanf("%d%d",&n,&m); for(int i = 1;i <= n;i ++) a[i] = i; rp[0] = new rope<int> (a,a + n + 1); for(int i = 1;i <= m;i ++) { rp[i] = new rope<int> (*rp[i - 1]); int opt; scanf("%d",&opt); if(opt == 1) { int a,b; scanf("%d%d",&a,&b); merge(i,a/* ^ lastans*/,b/* ^ lastans*/); } else if(opt == 2) { int k; scanf("%d",&k); rp[i] = rp[k/* ^ lastans*/]; } else { int a,b; scanf("%d%d",&a,&b); printf("%d\n",lastans = (find(i,a/* ^ lastans*/) == find(i,b/* ^ lastans*/))); } } return 0; }
例題三:
AHOI2006文本編輯器editor
題意
設計數據結構支持
插入刪除反轉字符串
分析:
由於rope的底層實現,insert,erase,get都是logn的
就是翻轉不行,不是自己手寫的打不了標記啊!!
怎么辦?
答:同時維護一正一反兩個rope……反轉即交換兩個子串……Orz……
區間循環位移?簡單,拆成多個子串連起來就好了……
區間a變b b變c c變d …… z變a? 呃……維護26個rope?
區間和?滾蛋,那是線段樹的活
區間kth?sorry,與數值有關的操作rope一概不支持……
5555 維修數列只能自己寫了……
ACCode:
#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; }
END