【BZOJ】3224 Tyvj 1728 普通平衡樹 平衡樹模板
好吧,我承認,我患有帕金森:手賤啊,打錯一個字元,把if (t[k].w<x)打成了if (t[k].w>x),結果只得了20分。(淚流滿面,55555……)
這道題考察的是平衡樹的模板,只有一些平衡樹的基本操作,相信只要對平衡樹有一些瞭解的同學就能把這題A掉。
做這道題,權當是複習一下平衡樹的模板,為接下來的平衡樹的學習和刷題打下基礎。
今天我主要講的是Treap。Treap,顧名思義,就是tree(二叉搜尋樹)+heap(堆),Treap這種資料結構即有二叉搜尋樹的有序性,並給予每個節點一個隨機鍵值,用堆的方式來維護整棵樹的平衡,防止了二叉搜尋樹退化成一條鏈,每次查詢的時間複雜度變成了O(n),使二叉搜尋樹的查詢的時間複雜度保持在O(log(n))左右。雖然對於一些較小的n,這個優化並不是非常明顯,但是隨著n的增大,Treap就會在效率上碾壓二叉搜尋樹。
Treap的基本操作有以下6個:
1、插入:將一個數插入Treap中,並使Treap的二叉搜尋樹和堆的性質保持不變。
2、刪除:將一個數從Treap中刪除,並使Treap的二叉搜尋樹和堆的性質保持不變。
3、求一個數在Treap變成一個有序序列後的排名
4、求在Treap變成一個有序序列後一個位置上的數
5、求一個數的前驅
6、求一個數的後繼
在維護Treap的堆性質的時候,會有一個操作:旋轉,就是把隨機鍵值進行維護堆的性質的操作。Treap的旋轉操作比Splay要簡單。
其實Treap說起來十分容易,但是敲起程式碼來卻並不簡單,程式碼量還是非常大的。
“以後打程式碼的時候一定要十分的認真。“不要把程式碼的準確度寄託在除錯上,只有靠自己清晰的思路。”——oyqy
不多說了,大家自己看吧。附上AC程式碼:
#include <cstdio> #include <cstdlib> using namespace std; struct note{ int w,size,rnd,g; }t[100010]; int n,son[100010][2],x,y,root,size,ans; void turn(int &k,int x){ int p=son[k][x]; son[k][x]=son[p][x^1]; son[p][x^1]=k; t[p].size=t[k].size; t[k].size=t[son[k][0]].size+t[son[k][1]].size+t[k].g; k=p; return; } void insert(int &k,int x){ if (k==0){ k=++size; t[k].w=x; t[k].rnd=rand(); t[k].g=t[k].size=1; return; } ++t[k].size; if (t[k].w==x) ++t[k].g; else if (t[k].w<x){ insert(son[k][1],x); if (t[son[k][1]].rnd<t[k].rnd) turn(k,1); } else { insert(son[k][0],x); if (t[son[k][0]].rnd<t[k].rnd) turn(k,0); } } void del(int &k,int x){ if (k==0) return; if (t[k].w==x){ if (t[k].g>1){ --t[k].g; --t[k].size; return; } if (son[k][0]*son[k][1]==0) k=son[k][0]+son[k][1]; else if (t[son[k][0]].rnd<t[son[k][1]].rnd) turn(k,0),del(k,x); else turn(k,1),del(k,x); } else { --t[k].size; if (t[k].w<x) del(son[k][1],x); else del(son[k][0],x); } return; } int query_pos(int k,int x){ if (k==0) return 0; if (t[k].w==x) return t[son[k][0]].size+1; else if (t[k].w<x) return t[son[k][0]].size+t[k].g+query_pos(son[k][1],x); else return query_pos(son[k][0],x); } int query_num(int k,int x){ if (k==0) return 0; if (t[son[k][0]].size>=x) return query_num(son[k][0],x); else if (t[son[k][0]].size+t[k].g<x) return query_num(son[k][1],x-t[son[k][0]].size-t[k].g); else return t[k].w; } void query_pre(int k,int x){ if (k==0) return; if (t[k].w<x) ans=k,query_pre(son[k][1],x); else query_pre(son[k][0],x); } void query_bac(int k,int x){ if (k==0) return; if (t[k].w>x) ans=k,query_bac(son[k][0],x); else query_bac(son[k][1],x); } int main(void){ scanf("%d",&n); for (int i=1; i<=n; ++i){ scanf("%d%d",&x,&y); switch (x){ case 1:insert(root,y);break; case 2:del(root,y);break; case 3:printf("%d\n",query_pos(root,y));break; case 4:printf("%d\n",query_num(root,y));break; case 5:query_pre(root,y);printf("%d\n",t[ans].w);ans=0;break; case 6:query_bac(root,y);printf("%d\n",t[ans].w);ans=0;break; } } return 0; }
3.30更新:
聽Manchery大神的指導,我又重新認識了treap的終極版:非旋轉treap。
雖然在洛谷和BZOJ上測出來都要比旋轉式的慢,但是非旋轉treap可以可持久化,所以還是有學習的必要的。
測評感悟:O2優化真的是個好東西,我在沒有開O2的洛谷上測出來用了1000ms+,而在開了O2的BZOJ上只用了500ms+。
附上AC程式碼:
#include <cstdio>
#include <algorithm>
using namespace std;
typedef pair<int,int> p;
struct note{
int ls,rs,w,rnd,size;
}t[100010];
int ti,x,o,root,len;
void updata(int k){
t[k].size=t[t[k].ls].size+t[t[k].rs].size+1;
}
p fl(int k,int x){
if (x==0) return make_pair(0,k);
int ls=t[k].ls,rs=t[k].rs;
if (x==t[ls].size) return t[k].ls=0,updata(k),make_pair(ls,k);
if (x==t[ls].size+1) return t[k].rs=0,updata(k),make_pair(k,rs);
if (x<t[ls].size){
p tmp=fl(ls,x);
return t[k].ls=tmp.second,updata(k),make_pair(tmp.first,k);
}
p tmp=fl(rs,x-t[ls].size-1);
return t[k].rs=tmp.first,updata(k),make_pair(k,tmp.second);
}
int hb(int x,int y){
if (x==0||y==0) return x+y;
if (t[x].rnd<t[y].rnd) return t[x].rs=hb(t[x].rs,y),updata(x),x;
else return t[y].ls=hb(x,t[y].ls),updata(y),y;
}
int wz(int k,int x){
int ans=0,tmp=2e9;
while (k){
if (x==t[k].w) tmp=min(tmp,ans+t[t[k].ls].size+1);
if (x>t[k].w) ans+=t[t[k].ls].size+1,k=t[k].rs;
else k=t[k].ls;
}
return tmp==2e9?ans:tmp;
}
int fd(int k,int x){
while (1){
if (t[t[k].ls].size==x-1) return t[k].w;
if (t[t[k].ls].size>x-1) k=t[k].ls;
else x-=t[t[k].ls].size+1,k=t[k].rs;
}
}
void ist(int x){
int k=wz(root,x);
p tmp=fl(root,k);
t[++len].w=x;
t[len].rnd=rand();
t[len].size=1;
root=hb(tmp.first,len);
root=hb(root,tmp.second);
}
void del(int x){
int k=wz(root,x);
p t1=fl(root,k),t2=fl(t1.first,k-1);
root=hb(t2.first,t1.second);
}
int pre(int k,int x){
int ans=-2e9;
while (k)
if (t[k].w<x) ans=max(ans,t[k].w),k=t[k].rs;
else k=t[k].ls;
return ans;
}
int bac(int k,int x){
int ans=2e9;
while (k)
if (t[k].w>x) ans=min(ans,t[k].w),k=t[k].ls;
else k=t[k].rs;
return ans;
}
int main(void){
scanf("%d",&ti);
while (ti--){
scanf("%d%d",&o,&x);
switch (o){
case 1:ist(x);break;
case 2:del(x);break;
case 3:printf("%d\n",wz(root,x));break;
case 4:printf("%d\n",fd(root,x));break;
case 5:printf("%d\n",pre(root,x));break;
case 6:printf("%d\n",bac(root,x));break;
}
}
return 0;
}
5.27更新:
總是聽其他大佬說起Splay有多好,但是由於我太菜了,一直都不會Splay。
昨天決定要好好學學Splay,對著Splay的模板就是一頓抄。
但可惜還是不能完全理解程式碼的意思,還是要多加思考和理解。
本來是昨天就想寫這篇部落格的,但是有同學讓我去幫他改程式碼,所以推遲到今天來寫了。
唉,自己還是太菜,幫別人改一個左偏樹的程式碼竟然花掉了我一個小時的時間……
今天在看這篇自己寫的部落格,發現只有旋轉型的Treap還依稀記得一點,非旋轉的Treap已是完全忘記。
蒟蒻還是要多複習自己之前所學,不然只能被其他大佬虐。
今日學習心得:%%%法老大佬,困擾我一個晚上的問題他只用了五分鐘就解決了——我的程式碼一直輸出太多或太少。
原來是因為我讀入優化的問題,我為了節省程式碼量,把處理負數的變數f定義為char型別的,然後在char型別前加了一個static。
說起來static到現在我還不知道是用來幹什麼用的,但是聽其他大佬說好像可以使程式碼的執行速度變快,所以就經常性的加上去了。
結果可想而知,就是這裡出錯了……
好像是因為static char f只能定義,不能修改之類的問題導致我沒有辦法讀入負數,然後就毫無疑問的一直WA了。
“沒事就不要用那些自己不熟練的東西,這些東西並沒有什麼卵用,沒法提升很多的執行速度,只是為了裝13罷了。”——法老大神。
附上AC程式碼:
#include <cstdio>
#include <cctype>
#define N 100010
using namespace std;
struct tree{
int w,size,g,f;
}t[N<<1];
int n,o,y,rt,size,ch[N][2];
inline char nc(){
static char ch[100010],*p1=ch,*p2=ch;
return p1==p2&&(p2=(p1=ch)+fread(ch,1,100010,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int& a){
static char c=nc();int f=1;
for (;!isdigit(c);c=nc()) if (c=='-') f=-1;
for (a=0;isdigit(c);a=a*10+c-'0',c=nc());
a*=f;return;
}
inline void updata(int x){
if (x){
t[x].size=t[x].g;
if (ch[x][0]) t[x].size+=t[ch[x][0]].size;
if (ch[x][1]) t[x].size+=t[ch[x][1]].size;
}
}
inline bool get(int x){
return ch[t[x].f][1]==x;
}
inline void rotate(int x){
int fa=t[x].f,ffa=t[fa].f,op=get(x);
ch[fa][op]=ch[x][op^1],t[ch[fa][op]].f=fa;
ch[x][op^1]=fa,t[fa].f=x;
t[x].f=ffa;
if (ffa) ch[ffa][ch[ffa][1]==fa]=x;
return updata(fa),updata(x);
}
inline void splay(int x){
for (int fa;fa=t[x].f;rotate(x))
if (t[fa].f) rotate((get(x)==get(fa))?fa:x);
rt=x;return;
}
inline void ist(int x){
if (!rt){
rt=++size;
ch[size][0]=ch[size][1]=t[size].f=0;
t[size].size=t[size].g=1,t[size].w=x;
return;
}
int now=rt,fa=0;
while (1){
if (x==t[now].w){
++t[now].g,updata(now),updata(fa),splay(now);
break;
}
fa=now,now=ch[now][t[now].w<x];
if (!now){
ch[fa][t[fa].w<x]=++size;
ch[size][0]=ch[size][1]=0;
t[size].f=fa,t[size].size=t[size].g=1,t[size].w=x;
updata(fa),splay(size);
break;
}
}
return;
}
inline void clean(int x){
ch[x][0]=ch[x][1]=t[x].w=t[x].f=t[x].g=t[x].size=0;
}
inline int find(int x){
int now=rt,ans=0;
while (1){
if (x<t[now].w) now=ch[now][0];
else {
ans+=(ch[now][0]?t[ch[now][0]].size:0);
if (x==t[now].w) {
splay(now);
return ans+1;
}
ans+=t[now].g;
now=ch[now][1];
}
}
}
inline int findx(int x){
int now=rt;
while (1){
if (ch[now][0]&&x<=t[ch[now][0]].size) now=ch[now][0];
else {
int tmp=(ch[now][0]?t[ch[now][0]].size:0)+t[now].g;
if (x<=tmp) return t[now].w;
x-=tmp,now=ch[now][1];
}
}
}
inline int pre(){
int now=ch[rt][0];
while (ch[now][1]) now=ch[now][1];
return now;
}
inline int bac(){
int now=ch[rt][1];
while (ch[now][0]) now=ch[now][0];
return now;
}
inline void del(int x){
find(x);
if (t[rt].g>1){
--t[rt].g,updata(rt);
return;
}
if (!ch[rt][0]&&!ch[rt][1]){
clean(rt),rt=0;
return;
}
if (!ch[rt][0]||!ch[rt][1]){
int p=rt;
rt=ch[rt][0]+ch[rt][1],t[rt].f=0,clean(p);
return;
}
int q=pre(),p=rt;
splay(q),ch[rt][1]=ch[p][1],t[ch[p][1]].f=rt;
return clean(p),updata(rt);
}
int main(void){
read(n);
while (n--){
read(o); read(y);
switch(o){
case 1: ist(y); break;
case 2: del(y); break;
case 3: printf("%d\n",find(y)); break;
case 4: printf("%d\n",findx(y)); break;
case 5: ist(y); printf("%d\n",t[pre()].w); del(y); break;
case 6: ist(y); printf("%d\n",t[bac()].w); del(y); break;
}
}
}
2018.1.7
以前的splay板子實在是太醜了,看不下去了,於是就把一直在用的splay板子貼上來吧。
話說求前驅和字尾最好splay一下是什麼鬼?保證複雜度什麼的好迷啊……還是遍歷一邊比較好想。
#include <cstdio>
#include <cctype>
using namespace std;
const int N=100010;
int t,o,x,f[N],w[N],ch[N][2],rt,size,sz[N];
inline char nc(void){
static char ch[100010],*p1=ch,*p2=ch;
return p1==p2&&(p2=(p1=ch)+fread(ch,1,100010,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &a){
static char c=nc();int f=1;
for (;!isdigit(c);c=nc()) if (c=='-') f=-1;
for (a=0;isdigit(c);a=(a<<3)+(a<<1)+c-'0',c=nc());
return (void)(a*=f);
}
inline void updata(int x){return (void)(sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1);}
inline void rotate(int x){
int y=f[x],op=(ch[y][1]==x);
ch[y][op]=ch[x][op^1];
if (ch[x][op^1]) f[ch[x][op^1]]=y;
f[x]=f[y];
if (f[y]) ch[f[y]][ch[f[y]][1]==y]=x;
f[y]=x,ch[x][op^1]=y;
return updata(y),updata(x);
}
inline void splay(int x,int ed){
for (int fa=f[x]; fa!=ed; rotate(x),fa=f[x])
if (f[fa]!=ed) rotate((x==ch[fa][0])==(fa==ch[f[fa]][0])?fa:x);
if (!ed) rt=x;
return;
}
inline void ist(int x){
int now=rt;
while (ch[now][w[now]<x]) now=ch[now][w[now]<x];
w[++size]=x,f[size]=now,ch[size][0]=ch[size][1]=0;
if (now) ch[now][w[now]<x]=size;
return splay(size,0);
}
inline void del(int x){
int now=rt;
while (w[now]!=x) now=ch[now][w[now]<x];
splay(now,0);
if (!ch[rt][0]) return (void)(f[rt=ch[rt][1]]=0);
now=ch[rt][0];
while (ch[now][1]) now=ch[now][1];
splay(now,rt);
f[ch[now][1]=ch[rt][1]]=now,f[rt=now]=0;
return updata(now);
}
inline int rank(int x){
int now=rt,ret=0;
while (now)
if (w[now]<x) ret+=sz[ch[now][0]]+1,now=ch[now][1];
else now=ch[now][0];
return ret+1;
}
inline int num(int x){
int now=rt;
while (x)
if (sz[ch[now][0]]+1<x) x-=sz[ch[now][0]]+1,now=ch[now][1];
else if (sz[ch[now][0]]+1==x) return w[now];
else now=ch[now][0];
return w[now];
}
inline int pre(int x){
int now=rt,ans=0;
while (now)
if (w[now]<x) ans=w[now],now=ch[now][1];
else now=ch[now][0];
return ans;
}
inline int suc(int x){
int now=rt,ans=0;
while (now)
if (w[now]>x) ans=w[now],now=ch[now][0];
else now=ch[now][1];
return ans;
}
int main(void){
for (read(t); t; --t){
read(o),read(x);
switch (o){
case 1: ist(x); break;
case 2: del(x); break;
case 3: printf("%d\n",rank(x)); break;
case 4: printf("%d\n",num(x)); break;
case 5: printf("%d\n",pre(x)); break;
case 6: printf("%d\n",suc(x)); break;
}
}
return 0;
}
相關推薦
【BZOJ】3224: Tyvj 1728 普通平衡樹
mes closed hid splay scanf 輸出 div spa opened 【題意】 1. 插入x數 2. 刪除x數(若有多個相同的數,因只刪除一個) 3. 查詢x數的排名(若有多個相同的數,因輸出最小的排名) 4. 查詢排名為x的數 5. 求x的前驅(前驅定
【BZOJ】3224 Tyvj 1728 普通平衡樹 平衡樹模板
好吧,我承認,我患有帕金森:手賤啊,打錯一個字元,把if (t[k].w<x)打成了if (t[k].w>x),結果只得了20分。(淚流滿面,55555……) 這道題考察的是平衡樹的模板,只有一些平衡樹的基本操作,相信只要對平衡樹有一些瞭解的同學就能把這題A掉。
【Splay】bzoj3224 Tyvj 1728 普通平衡樹
insert std mes dma space ota stream highlight can #include<cstdio> #include<iostream> #include<cstring> #include<al
【Treap】[BZOJ 3224]Tyvj 1728 普通平衡樹 & 非旋轉實現
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define maxn 2000005 struct Treap{
bzoj 3224: Tyvj 1728 普通平衡樹
print href 5% lin des pac amp lower tyvj Description 您需要寫一種數據結構(可參考題目標題),來維護一些數,其中需要提供以下操作:1. 插入x數2. 刪除x數(若有多個相同的數,因只刪除一個)3. 查詢x數的排名(若有多
bzoj 3224 Tyvj 1728 普通平衡樹
大小 題目 tput sub 最大的 數據結構 color see 相同 Tyvj 1728 普通平衡樹 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 17187 Solved: 7501[Submit][Statu
[bzoj] 3224 Tyvj 1728 普通平衡樹 || 平衡樹板子題
pan out mark blog make class while post bzoj #!/bin/bash g++ make.cpp -o make -Wall g++ 1.cpp -o ac -Wall g++ sb.cpp -o sb -Wall while
bzoj 3224: Tyvj 1728 普通平衡樹 && loj 104 普通平衡樹 (splay樹)
break lov pri pac erase names std namespace uniq 題目鏈接: https://www.lydsy.com/JudgeOnline/problem.php?id=3224 思路: splay樹模板題: 推薦博客:h
BZOJ - 3224 Tyvj 1728 普通平衡樹 splay
uil switch color rect 技術分享 onclick open des del splay的一道模板題 #include <algorithm> #include <iterator> #include <io
3224: Tyvj 1728 普通平衡樹
兩個 數據 std ont log 答案 數字 spl printf 3224: Tyvj 1728 普通平衡樹 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 14480 Solved: 6275 Descript
3224: Tyvj 1728 普通平衡樹(新板子)
pri ati urn 多個 stdin 目標 一個 題目 page 3224: Tyvj 1728 普通平衡樹 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 17048 Solved: 7429[Submit][S
【BZOJ】2595: [Wc2008]遊覽計劃-斯坦納樹&插頭DP
傳送門:bzoj2595 題解 法1:斯坦納樹 設 g [ i
【treap】【bzoj 3224】: Tyvj 1728 普通平衡樹
加上了前驅和後繼 //#define _TEST _TEST #include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #inc
【bzoj 3224】Tyvj 1728 普通平衡樹
Tyvj 1728 普通平衡樹 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 11767 Solved: 5021 [Submit][Status][Discuss] Description 您需要
Tyvj 1728 普通平衡樹
查詢 pre 序號 med esc desc 5% 其中 數據結構 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 13242 Solved: 5675[Submit][Status][Discuss] Descripti
【Splay】bzoj3223 Tyvj 1729 文藝平衡樹
ota ostream max play del || oot tyvj pla #include<cstdio> #include<iostream> #include<cstring> #include<algorithm&g
bzoj3224 Tyvj 1728 普通平衡樹
stat 基本 class rotate reg color space ins view 傳送門:http://www.lydsy.com/JudgeOnline/problem.php?id=3224 【題解】 寫起來跟*一樣,但是還是挺快調出來了。 主要就是每個數可以
[補檔][Tyvj 1728]普通平衡樹
name logs 題目 esp 最小 || 後繼 printf names 題目 您需要寫一種數據結構(可參考題目標題),來維護一些數,其中需要提供以下操作: 1. 插入x數 2. 刪除x數(若有多個相同的數,因只刪除一個) 3. 查詢x數的排名(若有多個相同的數,
7.Bzoj3224: Tyvj 1728 普通平衡樹(Treap)
Bzoj3224: Tyvj 1728 普通平衡樹(Treap) 終於自己敲出來了Treap。。。。。。拿來當板子還是挺好用的. 操作1,顯然根據二叉搜尋樹的性質直接建就好了. 操作2,如果有兩個兒子,則把這個點下旋下去,直到只有一個兒子或者沒有. 操作3,查詢比他小的數,記錄一個size,表示這個點有多少
絕對是全網最好的Splay 入門詳解——洛谷P3369&BZOJ3224: Tyvj 1728 普通平衡樹 包教包會
平衡樹是什麼東西想必我就不用說太多了吧。 百度百科: 一個月之前的某天晚上,yuli巨佬為我們初步講解了Splay,當時接觸到了平衡樹裡的旋轉等各種騷操作,感覺非常厲害。而第二天我調Splay的模板竟然就搞了一天,最後還是失敗告終,只能CV了事,而Splay也成了我心中的一個心