1. 程式人生 > >Luogu 3369 / BZOJ 3224 - 普通平衡樹 - [替罪羊樹]

Luogu 3369 / BZOJ 3224 - 普通平衡樹 - [替罪羊樹]

題目連結:

https://www.lydsy.com/JudgeOnline/problem.php?id=3224

https://www.luogu.org/problemnew/show/P3369

Description

您需要寫一種資料結構(可參考題目標題),來維護一些數,其中需要提供以下操作:
1. 插入x數
2. 刪除x數(若有多個相同的數,因只刪除一個)
3. 查詢x數的排名(若有多個相同的數,因輸出最小的排名)
4. 查詢排名為x的數
5. 求x的前驅(前驅定義為小於x,且最大的數)
6. 求x的後繼(後繼定義為大於x,且最小的數)

Input

第一行為n,表示操作的個數,下面n行每行有兩個數opt和x,opt表示操作的序號(1<=opt<=6)

Output

對於操作3,4,5,6每行輸出一個數,表示對應答案

Sample Input

10

1 106465

4 1

1 317721

1 460929

1 644985

1 84185

1 89851

6 81968

1 492737

5 493598

Sample Output

106465

84185

492737

HINT

1.n的資料範圍:n<=100000

2.每個數的資料範圍:[-2e9,2e9]

 

關於替罪羊樹:

替罪羊樹的主要思想就是將不平衡的樹壓成一個序列,然後暴力重構成一顆平衡的樹。

這裡的平衡指的是:對於某個 $0.5 \le \alpha \le 1$ 滿足 $size( ls(x) ) \le \alpha \cdot size(x)$ 並且 $size( rs(x) ) \le \alpha \cdot size(x)$。一般 $\alpha$ 取 $0.7 \sim 0.8$。

更加詳細的解釋和模板請參考替罪羊樹(重量平衡樹)入門

 

AC程式碼:

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+10;
const double alpha=0.8;
struct Node
{
    Node* ch[2]; //左右子節點
    int key,siz,cov; //key是值,siz是以該節點為根的樹的存在的節點數,cover是所有節點數量
    bool ext;
    void pushup() { //更新函式
        siz = ch[0]->siz + ch[1
]->siz + ext; cov = ch[0]->cov + ch[1]->cov + 1; } inline bool isbad() { //判斷是否要重構 return alpha*cov+5 < max(ch[0]->cov,ch[1]->cov); } }; struct ScapegoatTree { protected: Node mem[maxn]; //記憶體池 Node *tail,*null,*root; //tail為指向記憶體池元素的指標 Node *bak[maxn]; int baksz; //記憶體回收池 Node* newnode(int key) { Node* p=baksz?bak[--baksz]:tail++; p->ch[0] = p->ch[1] = null; p->siz = p->cov= p->ext = 1; p->key = key; return p; } void travel(vector<Node*>& v,Node* p) //中序遍歷將一棵樹轉化成序列 { if(p==null) return; travel(v,p->ch[0]); if(p->ext) v.push_back(p); else bak[baksz++]=p; travel(v,p->ch[1]); } Node* build(vector<Node*>& v,int l,int r) { if(l>=r) return null; int mid=(l+r)>>1; Node *p=v[mid]; p->ch[0] = build(v,l,mid); p->ch[1] = build(v,mid+1,r); p->pushup(); return p; } vector<Node*> cur; void rebuild(Node*& p) { cur.clear(); travel(cur,p); p=build(cur,0,cur.size()); } Node** insert(Node*& p,int val) { if(p==null) { p=newnode(val); return &null; } p->siz++, p->cov++; Node** res=insert(p->ch[val>=p->key],val); if(p->isbad()) res=&p; return res; } void erase(Node*& p,int k) { p->siz--; //維護siz int offset = p->ch[0]->siz + p->ext; //計算左子樹的存在的節點總數 if(p->ext && k==offset) p->ext=0; else { if(k<=offset) erase(p->ch[0],k); else erase(p->ch[1],k-offset); } } public: void init() { tail=mem; null=tail++; null->ch[0] = null->ch[1] = null; null->key = 0; null->siz = null->cov = null->ext = 0; root=null; //初始化根節點 baksz=0; //清空棧 } ScapegoatTree() { init(); } void insert(int val) { Node** res=insert(root,val); if(*res!=null) rebuild(*res); } int getrank(int val) { Node *p=root; int res=1; while(p!=null) { if(val <= p->key) p=p->ch[0]; else { res += p->ch[0]->siz + p->ext; p = p->ch[1]; } } return res; } int getkth(int k) { Node *p=root; while(p!=null) { if(p->ch[0]->siz+1==k && p->ext) return p->key; if(k <= p->ch[0]->siz) p=p->ch[0]; else k-=p->ch[0]->siz + p->ext, p=p->ch[1]; } } void delval(int val) { erase(root,getrank(val)); if(root->siz < alpha * root->cov) rebuild(root); } void delkth(int k) { erase(root,k); if(root->siz < alpha * root->cov) rebuild(root); } }st; int main() { int n,opt,x; scanf("%d",&n); while(n--) { scanf("%d%d",&opt,&x); if(opt==1) st.insert(x); if(opt==2) st.delval(x); if(opt==3) printf("%d\n",st.getrank(x)); if(opt==4) printf("%d\n",st.getkth(x)); if(opt==5) printf("%d\n",st.getkth(st.getrank(x)-1)); if(opt==6) printf("%d\n",st.getkth(st.getrank(x+1))); } }