[日常摸魚]bzoj3224普通平衡樹-Treap、Splay、01Trie、替罪羊樹…


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

經典的平衡樹模板題…各種平衡樹好像都可以(黃學長之前好像還用vector卡過了這題)

所以這篇博客也就來存一下模板什么的…

如果發現有什么地方講錯的還請留言懟我

 


 

 

1.Treap

首先是經典的Treap:Treap=Tree+heap

這里每個結點有兩個值$v$(結點的值)和$rnd$(一個隨機值),叫Treap的原因也就是它遵循二叉查找樹的性質($o$的左子樹的$v$<$o$的$v$<$o$右子樹的$v$)和堆的性質($o$左子樹的$rnd$>$o$的$rnd$且$o$右子樹的$rnd$>$o$的$rnd$,當然改成<也可以,不過這里我統一用>方便說)

二叉查找樹的性質在插入的時候直接維護:如果要插入的比當前小就丟給左孩子,大的話就丟給右孩子(相等的話就直接存這個點上,這里用一個變量$w$來記錄這個點實際上有幾個點)

堆的性質通過旋轉操作來維護:

比如如果你發現$o$的左孩子的$rnd$比$o$的$rnd$小了,它應該比較大才對,那這時候就可以把$o$右旋讓它的左孩子變成它的父親,可以發現這樣子做二叉搜索樹的性質並沒有被破壞,如果是右孩子$rnd$比$o$大的話同理就用左旋啦,靠這兩個操作就完成了堆性質的維護。因為用的是隨機數所以期望情況下效率還是不錯的。

(建議配合圖片食用) 

其他操作都比較簡單了…具體還是見代碼吧

#include<cstdio>
#include<cstdlib>
#define rep(i,n) for(register int i=1;i<=n;i++)
using namespace std;
inline int read()
{
    int s=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){s=s*10+c-'0';c=getchar();}
    return s*f;
}
const int N=100005; 
struct tree
{
    int s,w,rnd,l,r,v;
}tr[N];
int n,rot,cnt,op;
inline void updata(int x)
{
    tr[x].s=tr[tr[x].l].s+tr[tr[x].r].s+tr[x].w;
}
inline void rturn(int &x)
{
    int t=tr[x].l;tr[x].l=tr[t].r;tr[t].r=x;
    tr[t].s=tr[x].s;updata(x);x=t;
}
inline void lturn(int &x)
{
    int t=tr[x].r;tr[x].r=tr[t].l;tr[t].l=x;
    tr[t].s=tr[x].s;updata(x);x=t;
}
inline void insert(int &k,int val)
{
    if(k==0)
    {
        k=++cnt;tr[k].v=val;tr[k].w=tr[k].s=1;
        tr[k].rnd=rand();return;
    }
    tr[k].s++;
    if(tr[k].v==val)
        tr[k].w++;
    else if(tr[k].v<val)
    {
        insert(tr[k].r,val);
        if(tr[tr[k].r].rnd<tr[k].rnd)lturn(k);
    }else
    {
        insert(tr[k].l,val);
        if(tr[tr[k].l].rnd<tr[k].rnd)rturn(k);
    }
}
inline void del(int &k,int val)
{
    if(k==0)return;
    if(tr[k].v==val)
    {
        if(tr[k].w>1)
            tr[k].w--,tr[k].s--;
        else
        {
            if(tr[k].l*tr[k].r==0)
                k=tr[k].l+tr[k].r;
            else if(tr[tr[k].l].rnd<tr[tr[k].r].rnd)
                rturn(k),del(k,val);
            else
                lturn(k),del(k,val);
        }
    }else if(tr[k].v<val)
    {
        tr[k].s--;
        del(tr[k].r,val);
    }else
    {
        tr[k].s--;
        del(tr[k].l,val);
    }
}
inline int query_number_rank(int k,int val)
{
    if(k==0)return 0;
    if(tr[k].v==val)
        return tr[tr[k].l].s+1;
    else if(tr[k].v<val)
        return query_number_rank(tr[k].r,val)+tr[tr[k].l].s+tr[k].w;
    else
        return query_number_rank(tr[k].l,val);
}
inline void query_rank(int k,int x,int &ans)
{
    if(k==0)return;
    if(x<=tr[tr[k].l].s)
        query_rank(tr[k].l,x,ans);
    else if(tr[tr[k].l].s+tr[k].w<x)
        query_rank(tr[k].r,x-tr[tr[k].l].s-tr[k].w,ans);
    else ans=tr[k].v;
}
inline void query_pre(int k,int x,int &ans)
{
    if(k==0)return;
    if(tr[k].v<x)
    {
        ans=tr[k].v;
        query_pre(tr[k].r,x,ans);
    }else
        query_pre(tr[k].l,x,ans);
}
inline void query_sub(int k,int x,int &ans)
{
    if(k==0)return;
    if(tr[k].v>x)
    {
        ans=tr[k].v;
        query_sub(tr[k].l,x,ans);
    }else
        query_sub(tr[k].r,x,ans);
}
int main()
{
    srand(19260817);
    n=read();
    rep(i,n)
    {
        int x,ans=0;
        op=read();x=read();
        if(op==1)
            insert(rot,x);
        else if(op==2)
            del(rot,x);
        else if(op==3)
            printf("%d\n",query_number_rank(rot,x));
        else if(op==4)
        {
            query_rank(rot,x,ans);
            printf("%d\n",ans);
        }else if(op==5)
        {
            query_pre(rot,x,ans);
            printf("%d\n",ans);
        }else
        {
            query_sub(rot,x,ans);
            printf("%d\n",ans);
        }
    }
    return 0;
}
View Code

 

2.01Trie

別人博客的:http://www.cnblogs.com/KingSann/articles/7339563.html

有個神奇的東西叫01Trie…其實就是一顆字符集為$\{0,1 \}$的Trie。

然后我們把要存的變量搞成二進制丟到這顆Trie里面就行了!非常好寫。

缺點就是就是空間比較大,以及如果要存浮點數就有點無能為力了…(能不能寫成$a*10^b$的形式丟樹上呢…腦補ing…)

#include<cstdio>
const int N=100005*33;
const int T=(int)1e7;//偏移的大小 
const int B=31; 
inline int read()
{
    int s=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){s=s*10+c-'0';c=getchar();}
    return s*f;
}
int n,cnt=1,x,op;
int tr[N][2],f[N];
inline void insert(int val,int c)
{
    int k=1;val+=T;
    for(register int i=B;~i;i--)
    {
        int t=(val>>i)&1;
        if(!tr[k][t])tr[k][t]=++cnt;
        k=tr[k][t];f[k]+=c;
    }
}
inline int rank(int val)
{
    int k=1,res=0;val+=T;
    for(register int i=B;~i;i--)
    {
        int t=(val>>i)&1;
        if(t)res+=f[tr[k][0]];
        k=tr[k][t];
    }
    return res;
}
inline int kth(int val)
{
    int k=1,res=0;
    for(register int i=B;~i;i--)
    {
        if(val>f[tr[k][0]])res|=(1<<i),val-=f[tr[k][0]],k=tr[k][1];
        else k=tr[k][0];
    }
    res-=T;return res;
}
int main()
{
    n=read();
    while(n--)
    {
        op=read();x=read();
        if(op==1)insert(x,1);
        else if(op==2)insert(x,-1);
        else if(op==3)printf("%d\n",rank(x)+1);
        else if(op==4)printf("%d\n",kth(x));
        else if(op==5)printf("%d\n",kth(rank(x)));
        else if(op==6)printf("%d\n",kth(rank(x+1)+1));
    }
    return 0;
}
View Code

 

 

先寫到這了其他的有空再更…


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com