「模板」 FHQ_Treap 区间翻转

「模板」 FHQ_Treap 区间翻转


没有旋转的 Treap 实现区间操作的功能,很好理解,也很好写,只是速度不算太快。

对于要翻转的区间,把整棵 Treap(存有区间 \([1,n]\) 的信息)Split 成 \([1,l-1]\)\([l,r]\)\([r+1,n]\) 三部分,给中间部分的根节点打上标记,再一边下传标记一边 Merge 回来。

注意 Split 时,要按元素个数,不能按权值,因为元素个数可以通过维护节点信息的 size 域而直接得到,但随着区间的翻转,权值会乱套。

一定注意先推标记!!先推标记!!先推标记!!

就因为标记推晚了,我调了一天。

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
using std::swap;
const int MAXN=100010;
int n,m;
class FHQ_Treap
{
    public:
        FHQ_Treap(void)
        {
            rt=cnt=0;
            memset(a,0,sizeof a);
        }
        void Insert(int x)
        {
            s[++cnt]=node(x,Random(),1);
            Merge(rt,rt,cnt);
        }
        void Reverse(int x,int y)
        {
            int l=0,r=0,t=0;
            Split(rt,x-1,l,t),Split(t,y-x+1,t,r);
            s[t].lazy^=1,Merge(l,l,t),Merge(rt,l,r);
        }
        void Print(void)
        {
            DFS(rt),putchar('\n');
        }
    private:
        bool a[MAXN];
        int rt,cnt;
        struct node
        {
            int v,p,size,lazy,c[2];
            node(int _v=0,int _p=0,int _size=0)
            {
                v=_v,p=_p,size=_size,lazy=0;
                memset(c,0,sizeof c);
            }
        }s[MAXN];
        int Random(void)
        {
            int x;
            while(a[x=rand()%MAXN]);
            a[x]=1;
            return x;
        }
        void Update(int i)
        {
            s[i].size=s[s[i].c[0]].size+s[s[i].c[1]].size+1;
        }
        void PushDown(int i)
        {
            int &l=s[i].c[0],&r=s[i].c[1];
            swap(l,r);
            if(l)
                s[l].lazy^=1;
            if(r)
                s[r].lazy^=1;
            s[i].lazy=0;
        }
        void Split(int i,int x,int &l,int &r)
        {
            if(!i)
            {
                l=r=0;
                return;
            }
            if(s[i].lazy)
                PushDown(i);
            int t=s[s[i].c[0]].size+1;
            if(x<t)
                Split(s[r=i].c[0],x,l,s[i].c[0]);
            else
                Split(s[l=i].c[1],x-t,s[i].c[1],r);
            Update(i);
        }
        void Merge(int &i,int l,int r)
        {
            if(!l || !r)
            {
                i=l|r;
                return;
            }
            if(s[l].p>s[r].p)
            {
                if(s[l].lazy)
                    PushDown(l);
                Merge(s[i=l].c[1],s[l].c[1],r);
            }
            else
            {
                if(s[r].lazy)
                    PushDown(r);
                Merge(s[i=r].c[0],l,s[r].c[0]);
            }
            Update(i);
        }
        void DFS(int i)
        {
            if(s[i].lazy)
                PushDown(i);
            if(s[i].c[0])
                DFS(s[i].c[0]);
            printf("%d ",s[i].v);
            if(s[i].c[1])
                DFS(s[i].c[1]);
        }
}T;
int main(int argc,char *argv[])
{
    srand((unsigned)time(NULL));
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;++i)
        T.Insert(i);
    for(int i=1,l,r;i<=m;++i)
    {
        scanf("%d %d",&l,&r);
        T.Reverse(l,r);
    }
    T.Print();
    return 0;
}

谢谢阅读。

转载于:https://www.cnblogs.com/Capella/p/8506459.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值