NOIP回来就一直想着学平衡树。。。平衡树写久了调不出来真的会头脑发热.jpg
大概只写了几道题。。。
fhqtreap是不需要旋(xun)转(jun)的平衡树,仅使用分裂合并,一样可以保持平衡树的性质,并且可以非常简单地处理区间问题。
fhqtreap的核心有两端代码,split(分裂)和merge(合并)
split(x, l, r, k),表示把原x的子树以第k大数为界限,权值<=第k大数的数分在左子树,根为l,其他的分在右子树,根为r
void split(int x, int &l, int &r, int k) { if(!k) l=0, r=x; else if(k==tree[x].size) l=x, r=0; else if(k<=tree[lt].size) down(r=x), split(lt, l, lt, k), up(x); else down(l=x), split(rt, rt, r, k-tree[lt].size-1), up(x); }
merge(x, l, r),表示把根为l的子树和根为r的子树合并成一棵根为x的子树
void merge(int &x, int l, int r) { if(!l || !r) x=l+r; else if(tree[l].rnd<tree[r].rnd) down(x=l), merge(tree[x].rs, tree[x].rs, r), up(x); else down(x=r), merge(tree[x].ls, l, tree[x].ls), up(x); }
fhqtreap能实现哪些操作呢?
单点/区间插入,单点/区间删除,区间加,区间查询和,区间查询最值,区间反转,区间旋(xun)转(jun)(其实就是这个区间整体后移k步,超过区间的补到前面),还有等等...splay和线段树能做的大部分都能够做到...并且常数比splay小的多...
查询一个区间[l, r]就把一棵树split成三棵树,查中间那棵,再把它们merge回去
核心代码:
inline void add(int l, int r, int delta)//任意操作 { int x, y, z; split(root, x, y, r); split(x, z, x, l-1);//拆成z,x,y三棵 addone(x, delta);//任意操作 merge(x, z, x); merge(root, x, y);//并回去 }
随机数可以用rand()<<15|rand()来求
初始最好tree[0].rnd=tree[0].sum=tree[0].xxx=...=inf,否则up的时候可能求min会GG,同理求max要赋值-inf
例题时间~
例1 bzoj 3223 tyvj 1729
经典题。。。需要多运用一个rank查询x数的排名,才能进行split(否则得另写一个按数字分的split,相比起来这样更好写)
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<algorithm> #define ll long long #define lt tree[x].ls #define rt tree[x].rs using namespace std; const int maxn=500010, inf=1e9+1; int n, m, x, y, z, root, tott, tmp; struct treap{int rnd, mn, sum, delta, rev, size, ls, rs;} tree[maxn]; char s[20]; inline void read(int &k) { int f=1; k=0; char c=getchar(); while(c<‘0‘ || c>‘9‘) c==‘-‘&&(f=-1), c=getchar(); while(c<=‘9‘ && c>=‘0‘) k=k*10+c-‘0‘, c=getchar(); k*=f; } inline int min(int a, int b){return a<b?a:b;} inline void addone(int x, int delta) {if(!x) return; tree[x].sum+=delta; tree[x].delta+=delta; tree[x].mn+=delta;} inline void reverseone(int x) {tree[x].rev^=1; swap(lt, rt);} inline void build(int &x, int delta) {tree[x=++tott].rnd=rand()<<15|rand(); tree[x].sum=tree[x].mn=delta; tree[x].size=1;} inline void up(int x) { if(!x) return; tree[x].mn=min(tree[x].sum, min(tree[lt].mn, tree[rt].mn)); tree[x].size=tree[lt].size+tree[rt].size+1; } inline void down(int x) { if(!x) return; if(tree[x].delta) addone(lt, tree[x].delta), addone(rt, tree[x].delta); if(tree[x].rev) reverseone(lt), reverseone(rt); tree[x].delta=tree[x].rev=0; } void merge(int &x, int l, int r) { if(!l || !r) x=l+r; else if(tree[l].rnd<tree[r].rnd) down(x=l), merge(tree[x].rs, tree[x].rs, r), up(x); else down(x=r), merge(tree[x].ls, l, tree[x].ls), up(x); } void split(int x, int &l, int &r, int k) { if(!k) l=0, r=x; else if(k==tree[x].size) l=x, r=0; else if(k<=tree[lt].size) down(r=x), split(lt, l, lt, k), up(x); else down(l=x), split(rt, rt, r, k-tree[lt].size-1), up(x); } inline void insert(int pos, int delta) { int x, y; split(root, x, y, pos); merge(x, x, delta); merge(root, x, y); } inline void del(int pos) { int x, y, z; split(root, x, y, pos); split(x, x, z, pos-1); merge(root, x, y); } inline void add(int l, int r, int delta) { int x, y, z; split(root, x, y, r); split(x, z, x, l-1); addone(x, delta); merge(x, z, x); merge(root, x, y); } inline void reverse(int l, int r) { int x, y, z; split(root, x, y, r); split(x, z, x, l-1); reverseone(x); merge(x, z, x); merge(root, x, y); } inline void revolve(int l, int r, int delta) { int x, y, z, h; split(root, x, y, r-delta); split(x, z, x, l-1); split(y, y, h, delta); merge(x, y, x); merge(x, z, x); merge(root, x, h); } inline int query(int l, int r) { int x, y, z, ans; split(root, x, y, r); split(x, z, x, l-1); ans=tree[x].mn; merge(x, z, x); merge(root, x, y); return ans; } int main() { srand(19260817); read(n); tree[0].rnd=tree[0].mn=tree[0].sum=inf; for(int i=1;i<=n;i++) read(x), build(tmp, x), merge(root, root, tmp); read(m); for(int i=1;i<=m;i++) { scanf("%s", s+1); if(s[1]==‘A‘) read(x), read(y), read(z), add(x, y, z); else if(s[1]==‘R‘ && s[4]==‘O‘) read(x), read(y), read(z), revolve(x, y, z%(y-x+1)); else if(s[1]==‘R‘ && s[4]==‘E‘) read(x), read(y), reverse(x, y); else if(s[1]==‘D‘) read(x), del(x); else if(s[1]==‘I‘) read(x), read(y), build(tmp, y), insert(x, tmp); else read(x), read(y), printf("%d\n", query(x, y)); } }
例2 poj 3580
整合了大部分操作。。区间加,区间反转,区间旋(xun)转(jun),单点插入,单点删除,区间查询最小值
区间反转打标记就好了,down的时候交换左右子树,给左右子树打上标记即可,打标记的方式是^=1
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<algorithm> #define ll long long #define lt tree[x].ls #define rt tree[x].rs using namespace std; const int maxn=500010, inf=1e9+1; int n, m, x, y, z, root, tott, tmp; struct treap{int rnd, mn, sum, delta, rev, size, ls, rs;} tree[maxn]; char s[20]; inline void read(int &k) { int f=1; k=0; char c=getchar(); while(c<‘0‘ || c>‘9‘) c==‘-‘&&(f=-1), c=getchar(); while(c<=‘9‘ && c>=‘0‘) k=k*10+c-‘0‘, c=getchar(); k*=f; } inline int min(int a, int b){return a<b?a:b;} inline void addone(int x, int delta) {if(!x) return; tree[x].sum+=delta; tree[x].delta+=delta; tree[x].mn+=delta;} inline void reverseone(int x) {tree[x].rev^=1; swap(lt, rt);} inline void build(int &x, int delta) {tree[x=++tott].rnd=rand()<<15|rand(); tree[x].sum=tree[x].mn=delta; tree[x].size=1;} inline void up(int x) { if(!x) return; tree[x].mn=min(tree[x].sum, min(tree[lt].mn, tree[rt].mn)); tree[x].size=tree[lt].size+tree[rt].size+1; } inline void down(int x) { if(!x) return; if(tree[x].delta) addone(lt, tree[x].delta), addone(rt, tree[x].delta); if(tree[x].rev) reverseone(lt), reverseone(rt); tree[x].delta=tree[x].rev=0; } void merge(int &x, int l, int r) { if(!l || !r) x=l+r; else if(tree[l].rnd<tree[r].rnd) down(x=l), merge(tree[x].rs, tree[x].rs, r), up(x); else down(x=r), merge(tree[x].ls, l, tree[x].ls), up(x); } void split(int x, int &l, int &r, int k) { if(!k) l=0, r=x; else if(k==tree[x].size) l=x, r=0; else if(k<=tree[lt].size) down(r=x), split(lt, l, lt, k), up(x); else down(l=x), split(rt, rt, r, k-tree[lt].size-1), up(x); } inline void insert(int pos, int delta) { int x, y; split(root, x, y, pos); merge(x, x, delta); merge(root, x, y); } inline void del(int pos) { int x, y, z; split(root, x, y, pos); split(x, x, z, pos-1); merge(root, x, y); } inline void add(int l, int r, int delta) { int x, y, z; split(root, x, y, r); split(x, z, x, l-1); addone(x, delta); merge(x, z, x); merge(root, x, y); } inline void reverse(int l, int r) { int x, y, z; split(root, x, y, r); split(x, z, x, l-1); reverseone(x); merge(x, z, x); merge(root, x, y); } inline void revolve(int l, int r, int delta) { int x, y, z, h; split(root, x, y, r-delta); split(x, z, x, l-1); split(y, y, h, delta); merge(x, y, x); merge(x, z, x); merge(root, x, h); } inline int query(int l, int r) { int x, y, z, ans; split(root, x, y, r); split(x, z, x, l-1); ans=tree[x].mn; merge(x, z, x); merge(root, x, y); return ans; } int main() { srand(19260817); read(n); tree[0].rnd=tree[0].mn=tree[0].sum=inf; for(int i=1;i<=n;i++) read(x), build(tmp, x), merge(root, root, tmp); read(m); for(int i=1;i<=m;i++) { scanf("%s", s+1); if(s[1]==‘A‘) read(x), read(y), read(z), add(x, y, z); else if(s[1]==‘R‘ && s[4]==‘O‘) read(x), read(y), read(z), revolve(x, y, z%(y-x+1)); else if(s[1]==‘R‘ && s[4]==‘E‘) read(x), read(y), reverse(x, y); else if(s[1]==‘D‘) read(x), del(x); else if(s[1]==‘I‘) read(x), read(y), build(tmp, y), insert(x, tmp); else read(x), read(y), printf("%d\n", query(x, y)); } }
例3 bzoj 1251
除了最小值变最大值之外,操作是poj 3580的子集。。。直接搬一下就好。。。
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<algorithm> #define lt tree[x].ls #define rt tree[x].rs using namespace std; const int maxn=50010, inf=1e9+1; int n, m, ty, x, y, z, root, tott; struct treap{int rnd, mx, sum, delta, rev, size, ls, rs;} tree[maxn]; char buf[20000010],*ptr=buf-1; inline int read() { char c=*++ptr; int s=0,t=1; while(c<48||c>57) t=-1, c=*++ptr; while(c>=48&&c<=57) s=s*10+c-‘0‘, c=*++ptr; return s*t; } inline int max(int a, int b){return a>b?a:b;} inline void addone(int x, int delta) {tree[x].sum+=delta; tree[x].delta+=delta; tree[x].mx+=delta;} inline void reverseone(int x) {tree[x].rev^=1; swap(lt, rt);} inline void up(int x) { if(!x) return; tree[x].mx=max(tree[x].sum, max(tree[lt].mx, tree[rt].mx)); tree[x].size=tree[lt].size+tree[rt].size+1; } inline void down(int x) { if(!x) return; if(tree[x].delta) addone(lt, tree[x].delta), addone(rt, tree[x].delta); if(tree[x].rev) reverseone(lt), reverseone(rt); tree[x].delta=tree[x].rev=0; } void merge(int &x, int l, int r) { if(!l || !r) x=l+r; else if(tree[l].rnd<tree[r].rnd) down(x=l), merge(tree[x].rs, tree[x].rs, r), up(x); else down(x=r), merge(tree[x].ls, l, tree[x].ls), up(x); } void split(int x, int &l, int &r, int k) { if(!k) l=0, r=x; else if(k==tree[x].size) l=x, r=0; else if(k<=tree[lt].size) down(r=x), split(lt, l, lt, k), up(x); else down(l=x), split(rt, rt, r, k-tree[lt].size-1), up(x); } inline void add(int l, int r, int delta) { int x, y, z; split(root, x, y, r); split(x, z, x, l-1); addone(x, delta); merge(x, z, x); merge(root, x, y); } inline void reverse(int l, int r) { int x, y, z; split(root, x, y, r); split(x, z, x, l-1); reverseone(x); merge(x, z, x); merge(root, x, y); } inline int query(int l, int r) { int x, y, z, ans; split(root, x, y, r); split(x, z, x, l-1); ans=tree[x].mx; merge(x, z, x); merge(root, x, y); return ans; } int main() { fread(buf,1,sizeof(buf),stdin); n=read(); m=read(); tree[0].rnd=inf; tree[0].mx=tree[0].sum=-inf; for(int i=1;i<=n;i++) tree[++tott].size=1, tree[tott].rnd=rand()<<15|rand(), merge(root, root, tott); for(int i=1;i<=m;i++) { ty=read(); x=read(); y=read(); if(ty==1) z=read(), add(x, y, z); else if(ty==2) reverse(x, y); else printf("%d\n", query(x, y)); } }
例4 bzoj 3223
陶冶身心的水题。。。区间反转一个操作而已。。233
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<algorithm> #define ll long long #define lt tree[x].ls #define rt tree[x].rs using namespace std; const int maxn=500010, inf=1e9+1; int n, m, x, y, z, root, tott, tmp; struct treap{int rnd, sum, rev, size, ls, rs;} tree[maxn]; inline void read(int &k) { int f=1; k=0; char c=getchar(); while(c<‘0‘ || c>‘9‘) c==‘-‘&&(f=-1), c=getchar(); while(c<=‘9‘ && c>=‘0‘) k=k*10+c-‘0‘, c=getchar(); k*=f; } inline void reverseone(int x) {tree[x].rev^=1; swap(lt, rt);} inline void build(int &x, int delta) {tree[x=++tott].rnd=rand()<<15|rand(); tree[x].sum=delta; tree[x].size=1;} inline void up(int x) {if(!x) return; tree[x].size=tree[lt].size+tree[rt].size+1;} inline void down(int x) { if(!x) return; if(tree[x].rev) reverseone(lt), reverseone(rt); tree[x].rev=0; } void merge(int &x, int l, int r) { if(!l || !r) x=l+r; else if(tree[l].rnd<tree[r].rnd) down(x=l), merge(tree[x].rs, tree[x].rs, r), up(x); else down(x=r), merge(tree[x].ls, l, tree[x].ls), up(x); } void split(int x, int &l, int &r, int k) { if(!k) l=0, r=x; else if(k==tree[x].size) l=x, r=0; else if(k<=tree[lt].size) down(r=x), split(lt, l, lt, k), up(x); else down(l=x), split(rt, rt, r, k-tree[lt].size-1), up(x); } inline void reverse(int l, int r) { int x, y, z; split(root, x, y, r); split(x, z, x, l-1); reverseone(x); merge(x, z, x); merge(root, x, y); } void print(int x) { if(!x) return; down(x); print(lt); printf("%d ", tree[x].sum); print(rt); } int main() { srand(19260817); read(n); read(m); tree[0].rnd=tree[0].sum=inf; for(int i=1;i<=n;i++) build(tmp, i), merge(root, root, tmp); for(int i=1;i<=m;i++) read(x), read(y), reverse(x, y); print(root); }
例5 bzoj 1500
...大boss,头皮发麻过几天再补,需要垃圾回收,线性建树,让我冷静一下...