$fhq$ $treap$

\(fhq~~treap\)

\(fhq~Treap\) 是一种很好用的数据结构。它很像 $ Treap$ ,但它也可以支持区间操作,同时它也可以进行可持久化,它甚至还很好写。面对这么好的数据结构,怎么能不学一学呢?

这个ppt可能是起源?

​ 如果你已经看过了上面那个 \(ppt\),应该知道无旋 \(Treap\) 的思想是不修改,只新建和重用。

\(\text{代替} ~treap\)

\(split\)

​ 首先它有一个有趣的操作 \(split\),意思是把一棵树分裂成两棵树。嗯...?其实相当于新建两棵树,它们拼起来等于原来那棵树。两种说法有区别吗?当然有啦,注意,“不修改!“,如果直接把树变成两棵,显然就是修改了,而用两棵新的树表示它,对它并没有什么影响。这里可能说的有点啰嗦了,但是我认为对于后面的理解还是有好处的。不过,分裂后可能确实会对原树进行一些更改,但是并不影响它二叉搜索树的性质。

​ 分裂也有两种:

​ 1、将权值小于等于k的节点分裂出来。

void split (int n,int k,int &x,int &y)
{
    if(!n) { x=y=0; return; }
    if(v[n]<=k) x=n,split(ch[x][1],k,ch[x][1],y);
    else y=n,split(ch[y][0],k,x,ch[y][0]);
    update(n);
}

​ 2、将前k个节点分裂出来。

void split (int n,int k,int &x,int &y)
{
    if(!n) { x=y=0; return; }
    if(d[n]) pushdown(n);
    if(s[ ch[n][0] ]<k)
        x=n,split(ch[x][1],k-s[ ch[x][0] ]-1,ch[x][1],y);
    else
        y=n,split(ch[y][0],k,x,ch[y][0]);
    update(n);
}

​ 第一个主要用于维护数集,第二个主要用于维护序列。

\(merge\)

​ 字面意思,就是把两棵树合并。不过这个操作限制挺多的,要求某一棵树完全小于另一树,也就是说,在不考虑随机权值的情况下可以随便合并。为了维护平衡,我们按照随机权值来合并。看代码:

int mer (int x,int y)
{
    if(!x||!y) return x|y;
    if(r[x]<r[y])
    {
        ch[x][1]=mer(ch[x][1],y);
        update(x); return x;
    }
    else
    {
        ch[y][0]=mer(x,ch[y][0]);
        update(y); return y;
    }
}

\(insert\)

​ 剩下的操作真是一个比一个简单。如何插入?首先把小于它的分裂出来命名为 \(a\) ,剩下的命名为 \(b\),然后给它新建一个节点 \(c\),最后 \(rt=mer(mer(a,b),c)\)

\(delete\)

​ 小于它的分出来,大于它的分出来,等于它的随便删一个,再按顺序 \(merge\) 回来;

\(rank\)

​ 比它小的分出来,查一下 \(size\) 再+1;

\(kth\)

​ 如果无聊的话,可以尝试分裂前 \(k\) 大,然而直接在树上走着找就可以了,因为树高是 \(log\) 的;

\(pre\)

​ 比它小的分裂出来,查询最大的那个数;

\(nex\)

​ 比它大的分裂出来,查询最小的那个数;

\(code:\)

# include <cstdio>
# include <iostream>
# include <cstdlib>
# include <ctime>
# define R register int
# define getchar() (S==T&&(T=(S=BB)+fread(BB,1,1<<20,stdin),S==T)?EOF:*S++)
char BB[1 << 20], *S = BB, *T = BB; 

using namespace std;

const int N=100005;
int ch[N][2],r[N],s[N],v[N];
int n,rt,cnt,a,b,opt,x;

void update (int x) { s[x]=1+s[ ch[x][0] ]+s[ ch[x][1] ]; }

inline int rand()
{
    static int seed=2003;
    return seed=int(seed*48271LL%2147483647);
}

int mer (int x,int y)
{
    if(!x||!y) return x|y;
    if(r[x]<r[y])
    {
        ch[x][1]=mer(ch[x][1],y);
        update(x); return x;
    }
    else
    {
        ch[y][0]=mer(x,ch[y][0]);
        update(y); return y;
    }
}

void spilt (int n,int k,int &x,int &y)
{
    if(!n) { x=y=0; return; }
    if(v[n]<=k) x=n,spilt(ch[x][1],k,ch[x][1],y);
    else y=n,spilt(ch[y][0],k,x,ch[y][0]);
    update(n);
}

int kth (int n,int k)
{
    while(1)
    {
        if(k<=s[ ch[n][0] ]) n=ch[n][0];
        else if(k==s[ ch[n][0] ]+1) return n;
        else k-=s[ ch[n][0] ]+1,n=ch[n][1];
    }
}

int newnode (int x)
{
    s[++cnt]=1;
    r[cnt]=rand();
    v[cnt]=x;
    return cnt;
}

int read()
{
    R x=0,f=1;
    char c=getchar();
    while (!isdigit(c)) { if(c=='-') f=-f; c=getchar(); }
    while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}

int main()
{
    n=read();
    for (R i=1;i<=n;++i)
    {
        opt=read(); x=read();
        if(opt==1)
        {
            spilt(rt,x,a,b);
            rt=mer(mer(a,newnode(x)),b);
        }
        else if(opt==2)
        {
            spilt(rt,x,a,b);
            spilt(a,x-1,a,rt);
            rt=mer(ch[rt][0],ch[rt][1]);
            rt=mer(mer(a,rt),b);
        }
        else if(opt==3)
        {
            spilt(rt,x-1,a,b);
            printf("%d\n",s[a]+1);
            rt=mer(a,b);
        }
        else if(opt==4)
        {
            printf("%d\n",v[kth(rt,x)]);
        }
        else if(opt==5)
        {
            spilt(rt,x-1,a,b);
            printf("%d\n",v[kth(a,s[a])]);
            rt=mer(a,b);
        }
        else
        {
            spilt(rt,x,a,b);
            printf("%d\n",v[kth(b,1)]);
            rt=mer(a,b);
        }
    }
    return 0;
}

\(\text{代替} ~Splay\)

​ 这里的 \(Splay\) 特指用来做序列操作的那种;

​ 其实,想一想就能知道 \(fhq~treap\) 应该是很适合序列操作的,尤其是 \(split\) 操作,简直就是为了序列操作量身定制的。

\(reverse\)

​ 因为是序列上的某一段,所以要按照大小分裂。先把前 \(l-1\) 个分裂出来,再把第二段的前 \(r-l+1\) 个分出来,打上翻转标记,最后按顺序 \(merge\) 就可以了。在 \(split\)\(merge\) 里,都要记得下放标记;

\(code:\)

# include <cstdio>
# include <iostream>
# define R register int

using namespace std;

const int N=100005;
int n,m,cnt,a,b,c,rt,x,y;
int s[N],r[N],v[N],d[N],ch[N][2];

inline int rd()
{
    static int seed=2003;
    return seed=int(seed*48271LL%2147483647);
}

int newnode (int x)
{
    s[++cnt]=1;
    r[cnt]=rd();
    v[cnt]=x;
    d[cnt]=0;
    return cnt;
}

void update (int x) { s[x]=1+s[ ch[x][0] ]+s[ ch[x][1] ]; }

void pushdown (int x)
{
    swap(ch[x][0],ch[x][1]);
    d[x]=0;
    if(ch[x][0]) d[ ch[x][0] ]^=1;
    if(ch[x][1]) d[ ch[x][1] ]^=1;
}

int mer (int x,int y)
{
    if(!x||!y) return x|y;
    if(r[x]<r[y])
    {
        if(d[x]) pushdown(x);
        ch[x][1]=mer(ch[x][1],y);
        update(x); return x;
    }
    else
    {
        if(d[y]) pushdown(y);
        ch[y][0]=mer(x,ch[y][0]);
        update(y); return y;
    }
}

void spilt (int n,int k,int &x,int &y)
{
    if(!n) { x=y=0; return; }
    if(d[n]) pushdown(n);
    if(s[ ch[n][0] ]<k)
    {
        x=n;
        spilt(ch[x][1],k-s[ ch[x][0] ]-1,ch[x][1],y);
    }
    else
    {
        y=n;
        spilt(ch[y][0],k,x,ch[y][0]);
    }
    update(n);
}

void write (int x)
{
	if(d[x]) pushdown(x);
    if(ch[x][0]) write(ch[x][0]);
    printf("%d ",v[x]);
    if(ch[x][1]) write(ch[x][1]);
}

int main()
{
    scanf("%d%d",&n,&m);
    for (R i=1;i<=n;++i)
		rt=mer(rt,newnode(i));
    for (R i=1;i<=m;++i)
    {
        scanf("%d%d",&x,&y);
        spilt(rt,x-1,a,b);
        spilt(b,y-x+1,b,c);
        d[b]^=1;
		rt=mer(mer(a,b),c);
    }
    write(rt);
    return 0;
}

看到这里,你可能会觉得这个东西没什么用处啊,学个 \(Splay\) 不就好了?

嗯。。。你的 \(Splay\) 会可持久化吗?

\(\text{可持久化}~treap\)

# include <cstdio>
# include <iostream>
# define R register int
# define inf 2147483647

using namespace std;

const int N=500005;
const int M=500005*50;
int n,cnt,a,b,c;
int rt[N],ch[M][2],s[M],r[M],v[M];

inline int rd()
{
    static int seed=2003;
    return seed=int(seed*48271LL%2147483647);
}

int newnode (int x)
{
    s[++cnt]=1;
    v[cnt]=x;
    r[cnt]=rd();
    return cnt;
}

void update (int x) { s[x]=1+s[ ch[x][0] ]+s[ ch[x][1] ]; }

void cop (int a,int b)
{
    ch[a][0]=ch[b][0];
    ch[a][1]=ch[b][1];
    r[a]=r[b];
    s[a]=s[b];
    v[a]=v[b];
}

void split (int n,int k,int &x,int &y)
{
    if(!n) { x=y=0; return; }
    if(v[n]<=k)
    {
        x=++cnt; cop(x,n);
        split(ch[x][1],k,ch[x][1],y);
        update(x);
    }
    else
    {
        y=++cnt; cop(y,n);
        split(ch[y][0],k,x,ch[y][0]);
        update(y);
    }
}

int mer (int x,int y)
{
    if(!x||!y) return x+y;
    if(r[x]<r[y])
    {
        int t=++cnt; cop(t,x);
        ch[t][1]=mer(ch[t][1],y);
        update(t); return t;
    }
    else
    {
        int t=++cnt; cop(t,y);
        ch[t][0]=mer(x,ch[t][0]);
        update(t); return t;
    }
}

int kth (int n,int k)
{
    while(1)
    {
        if(k<=s[ ch[n][0] ]) n=ch[n][0];
        else if(k==s[ ch[n][0] ]+1) return n;
        else k-=s[ ch[n][0] ]+1,n=ch[n][1];
    }
}

int main()
{
    scanf("%d",&n);
    int las,opt,x;
    for (R i=1;i<=n;++i)
    {
        scanf("%d%d%d",&las,&opt,&x);
        switch(opt)
        {
            case 1:
            {
                split(rt[las],x,a,b);
                rt[i]=mer(mer(a,newnode(x)),b);
                break;
            }
            case 2:
            {
                split(rt[las],x-1,a,b);
                split(b,x,b,c);
                b=mer(ch[b][0],ch[b][1]);
                rt[i]=mer(mer(a,b),c);
                break;
            }
            case 3:
            {
                rt[i]=rt[las];
                split(rt[i],x-1,a,b);
                printf("%d\n",s[a]+1);
                rt[i]=mer(a,b);
                break;
            }
            case 4:
            {
                rt[i]=rt[las];
                printf("%d\n",v[ kth(rt[i],x) ]);
                break;
            }
            case 5:
            {
                rt[i]=rt[las];
                split(rt[i],x-1,a,b);
                if(!a) printf("%d\n",inf);
                else printf("%d\n",v[ kth(a,s[a]) ]);
                rt[i]=mer(a,b);
                break;
            }
            case 6:
            {
                rt[i]=rt[las];
                split(rt[i],x,a,b);
                if(!b) printf("%d\n",-inf);
                else printf("%d\n",v[ kth(b,1) ]);
                rt[i]=mer(a,b);
                break;
            }
        }
    }
    return 0;
}

\(\text{可持久化}~Splay\):

# include <cstdio>
# include <iostream>
# define R register int
# define ll long long
# define getchar() (S==T&&(T=(S=BB)+fread(BB,1,1<<20,stdin),S==T)?EOF:*S++)
char BB[1 << 20], *S = BB, *T = BB; 

using namespace std;

const int N=200005;
const int M=20000007;
int n,las,opt,a,b,c,cnt;
int rt[N],ch[M][2],siz[M],r[M],rev[M],v[M];
ll ans,x,y,pos,s[M];

inline ll read()
{
    ll x=0,f=1;
    char c=getchar();
    while (!isdigit(c)) { if(c=='-') f=-f; c=getchar(); }
    while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}

inline int rd()
{
    static int seed=2003;
    return seed=int(seed*48271LL%2147483647);
}

int newnode (int x)
{
    siz[++cnt]=1;
    s[cnt]=x;
    v[cnt]=x;
    r[cnt]=rd();
    return cnt;
}

void update (int x)
{
    siz[x]=1+siz[ ch[x][0] ]+siz[ ch[x][1] ];
    s[x]=v[x]+s[ ch[x][0] ]+s[ ch[x][1] ];
}

int cop (int x,int y)
{
    siz[x]=siz[y]; r[x]=r[y]; s[x]=s[y];
    rev[x]=rev[y]; v[x]=v[y]; 
    ch[x][0]=ch[y][0]; 
    ch[x][1]=ch[y][1];
    return x;
}

void pushdown (int x)
{
    if(!rev[x]) return;
    rev[x]=0;
    if(ch[x][0]) ch[x][0]=cop(++cnt,ch[x][0]);
    if(ch[x][1]) ch[x][1]=cop(++cnt,ch[x][1]);
    swap(ch[x][0],ch[x][1]);
    if(ch[x][0]) rev[ ch[x][0] ]^=1;
    if(ch[x][1]) rev[ ch[x][1] ]^=1;
}

void split (int n,int k,int &x,int &y)
{
    if(!n) { x=y=0; return; }
    pushdown(n);
    if(k<=siz[ ch[n][0] ])
    {
        y=++cnt; y=cop(y,n);
        split(ch[y][0],k,x,ch[y][0]);
        update(y);
    }
    else
    {
        x=++cnt; x=cop(x,n);
        split(ch[x][1],k-siz[ ch[x][0] ]-1,ch[x][1],y);
        update(x);
    }
}

int mer (int a,int b)
{
    if(!a||!b) return a|b;
    pushdown(a); pushdown(b);
    if(r[a]<r[b])
    {
        int t=++cnt; t=cop(t,a);
        ch[t][1]=mer(ch[t][1],b);
        update(t); return t;
    }
    else
    {
        int t=++cnt; t=cop(t,b);
        ch[t][0]=mer(a,ch[t][0]);
        update(t); return t;
    }
    
}

int main()
{
    n=read();
    for (R i=1;i<=n;++i)
    {
        las=read(); opt=read();
        switch (opt)
        {
            case 1:
            {
                pos=read(); x=read();
                pos^=ans; x^=ans;
                split(rt[las],pos,a,b);
                rt[i]=mer(mer(a,newnode(x)),b);
                break;
            }
            case 2:
            {
                pos=read(); pos^=ans;
                split(rt[las],pos-1,a,b);
                split(b,1,b,c);
                rt[i]=mer(a,c);
                break;
            }
            case 3:
            {
                x=read(); y=read();
                x^=ans; y^=ans;
                split(rt[las],x-1,a,b);
                split(b,y-x+1,b,c);
                rev[b]^=1;
                rt[i]=mer(mer(a,b),c);
                break;
            }
            case 4:
            {
                x=read(); y=read();
                x^=ans; y^=ans;
                rt[i]=rt[las];
                split(rt[las],x-1,a,b);
                split(b,y-x+1,b,c);
                ans=s[b];
                printf("%lld\n",ans);
                break;
            }
        }
    }
    return 0;
}
posted @ 2019-07-18 21:47  shzr  阅读(301)  评论(0编辑  收藏  举报