treap
您須要寫一種數據結構(可參考題目標題),來維護一些數,其中須要提供如下操做:插入x數,刪除x數(如有多個相同的數,因只刪除一個),查詢x數的排名(排名定義爲比當前數小的數的個數+1。如有多個相同的數,因輸出最小的排名),查詢排名爲x的數,求x的前驅(前驅定義爲小於x,且最大的數),求x的後繼(後繼定義爲大於x,且最小的數)。c++
treap彷佛就是一個保持堆性質的二叉搜索樹。結點的優先級是隨機分配的。能夠保證treap的全部操做,指望時間複雜度都爲logn。git
代碼中大量運用了遞歸操做。注意一個坑點是刪除操做的最後須要update一下。數據結構
#include <ctime> #include <cctype> #include <cstdio> #include <cstdlib> #include <algorithm> using namespace std; const int maxn=1e5+5, INF=1e9; int randint(){ //比手寫rand效果好 return rand(); } void get(int &x){ static int flag; static char c; flag=1; //static的變量只初始化一次 for (c=getchar(); !isdigit(c); c=getchar()) if (c=='-') flag=-1; for (x=c-48; c=getchar(), isdigit(c); ) x=(x<<3)+(x<<1)+c-48; x*=flag; } struct Treap{ //時刻牢記旋轉操做不破壞bst的性質 //小根堆 x:結點編號 //空間複雜度:插入次數 //v:結點的值 w:結點的優先級 int tot, root, siz[maxn], v[maxn], w[maxn], s[maxn][2]; void up(int x){ siz[x]=siz[s[x][0]]+siz[s[x][1]]+1; } void spin(int &x, int p){ //把x所指的p孩子旋轉上來 int t=s[x][p]; s[x][p]=s[t][!p]; s[t][!p]=x; //重構關係 up(x); up(t); x=t; //維護子樹大小 將當前位置更換 } void ins(int &x, int c){ //不停把優先級小的轉上來 if (!x){ x=++tot; siz[x]=1; v[x]=c; w[x]=randint(); return; } ++siz[x]; if (c<=v[x]){ ins(s[x][0], c); //左邊是v小於等於v[x]的結點 if (w[s[x][0]]<w[x]) spin(x, 0); } else { ins(s[x][1], c); if (w[s[x][1]]<w[x]) spin(x, 1); } } void del(int &x, int c){ //把這個數一直轉到葉子節點而後刪除 if (v[x]==c){ if (!s[x][0]||!s[x][1]){ x=s[x][0]+s[x][1]; return; } if (w[s[x][0]]>w[s[x][1]]){ spin(x, 1); del(s[x][0], c); } else{ spin(x, 0); del(s[x][1], c); } } //別忘記這裏也要update! else if (v[x]>c) del(s[x][0], c); else del(s[x][1], c); up(x); //因爲沒有旋轉操做須要手動維護 } int rank(int x, int c){ //詢問c的最小排名 if (!x) return 1; if (c<=v[x]) return rank(s[x][0], c); else return rank(s[x][1], c)+siz[s[x][0]]+1; } int atrank(int x, int c){ //詢問第c個數 if (siz[s[x][0]]==c-1) return v[x]; if (siz[s[x][0]]>=c) return atrank(s[x][0], c); else return atrank(s[x][1], c-siz[s[x][0]]-1); } int pre(int x, int c){ //詢問小於c且最大的數 if (!x) return -INF; if (v[x]<c) return max(v[x], pre(s[x][1], c)); //答案有多是根 else return pre(s[x][0], c); } int nxt(int x, int c){ //詢問大於c且最小的數 if (!x) return INF; if (v[x]>c) return min(v[x], nxt(s[x][0], c)); //答案有多是根 else return nxt(s[x][1], c); } }treap; int n, op, x; int main(){ get(n); srand(time(NULL)); for (int i=0; i<n; ++i){ get(op); get(x); if (op==1) treap.ins(treap.root, x); if (op==2) treap.del(treap.root, x); if (op==3) printf("%d\n", treap.rank(treap.root, x)); if (op==4) printf("%d\n", treap.atrank(treap.root, x)); if (op==5) printf("%d\n", treap.pre(treap.root, x)); if (op==6) printf("%d\n", treap.nxt(treap.root, x)); } return 0; }