• 平衡树 fhqTreap 区间操作


    注:由于本代码注释默认各位都已理解了fhqTreap的基本操作,因此本代码对于例如split,merge等基本

        函数的注释未曾给出。若您看不懂本代码,您可以先移步这里

    //Treap fhq版(不旋转) 
    //此模板为平衡树维护区间操作的模板 
    //注:在区间操作中split()标准变为子树大小 
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #define INF 0x3f3f3f3f
    #define MAXN 500001
    using namespace std;
    int n,m,a[MAXN],cnt,size,root;
    int son[MAXN][2],siz[MAXN],val[MAXN],sum[MAXN],rd[MAXN];
    int lazy_revise[MAXN],lazy_reverse[MAXN];//记录当前节点是否需要修改或翻转 
    int tmx[MAXN],lmx[MAXN],rmx[MAXN];//求最大子段和专用数组 
    //当前节点最大子段和,左儿子最大子段和,右儿子最大子段和 
    queue<int> trashcan;//回收节点重复利用 
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void update(int x)
    {
        if(son[x][0]&&son[x][1])
        {
            siz[x]=siz[son[x][0]]+siz[son[x][1]]+1;
            sum[x]=sum[son[x][0]]+sum[son[x][1]]+val[x];
            tmx[x]=max(tmx[son[x][0]],tmx[son[x][1]]);
            tmx[x]=max(tmx[x],rmx[son[x][0]]+val[x]+lmx[son[x][1]]);
            lmx[x]=max(lmx[son[x][0]],sum[son[x][0]]+val[x]+lmx[son[x][1]]);
            rmx[x]=max(rmx[son[x][1]],sum[son[x][1]]+val[x]+rmx[son[x][0]]);
        }
        if(son[x][0]&&!son[x][1])
        {
            siz[x]=siz[son[x][0]]+1;
            sum[x]=sum[son[x][0]]+val[x];
            tmx[x]=max(tmx[son[x][0]],rmx[son[x][0]]+val[x]);
            lmx[x]=max(lmx[son[x][0]],sum[son[x][0]]+val[x]);
            lmx[x]=max(0,lmx[x]);
            rmx[x]=max(0,val[x]+rmx[son[x][0]]);
        }
        if(!son[x][0]&&son[x][1])
        {
            siz[x]=siz[son[x][1]]+1;
            sum[x]=sum[son[x][1]]+val[x];
            tmx[x]=max(tmx[son[x][1]],lmx[son[x][1]]+val[x]);
            rmx[x]=max(rmx[son[x][1]],sum[son[x][1]]+val[x]);
            rmx[x]=max(0,rmx[x]);
            lmx[x]=max(0,lmx[son[x][1]]+val[x]);
        }
        if(!son[x][0]&&!son[x][1])
        {
            siz[x]=1,sum[x]=tmx[x]=val[x];
            lmx[x]=rmx[x]=max(val[x],0);
        }
    }
    int new_node(int k)
    {
        int x=0;
        if(!trashcan.empty())
        {
            x=trashcan.front();
            trashcan.pop();
        }
        else x=++cnt;
        son[x][0]=son[x][1]=0;
        lazy_reverse[x]=0;
        lazy_revise[x]=INF;
        rd[x]=rand();
        siz[x]=1;
        val[x]=sum[x]=tmx[x]=k;
        lmx[x]=rmx[x]=max(k,0);
        return x; 
    }
    int build(int *data,int k)//类似于笛卡尔树的建树方式 
    //笛卡尔树的建树方式如下(笛卡尔树val值满足小根堆 随机数值满足二叉查找树) 
    //前提新节点插入顺序必须val值从小到大插入 以保证新入节点val值必比原有节点大 
    //栈内存极右链 即从栈底到栈顶存储根节点 根节点的右儿子 根节点的右儿子的右儿子 显然随机数值从栈顶到栈底递减 
    //进入一个新节点后 从栈顶开始搜索 找到栈中第一个比新入节点随机数值小的数 
    //由于随机数值小 因此新入节点是找到的节点的儿子 又由于val值大 因此新入节点是找到的节点的右儿子 
    //又因为新入节点的val值大于找到的节点的右儿子的val值 因此原右儿子成为新入节点的左儿子 
    //由于原右儿子即为找到的节点在栈内上方的元素 因此把此元素修改至左儿子后 把此元素及其上方元素出栈即可 
    //由于栈中存极右链 因此找到的节点将在栈中位于找到的节点的上方 即替代原右儿子的位置 
    //直至所有点均插入即可 
    {
        int now=0,pre=0;//当前节点,找到的节点的原右儿子 
        static int sta[MAXN],top=0;
        //static:静态变量 其在定义时系统将自动将其初始化为0 另外在调用完后不会直接销毁 而会在下次重新定义同名静态变量时重新将以前定义过的变量调用 同时保留上次调用完后变量内存放的数据 
        for(int i=1;i<=k;i++)
        {
            now=new_node(data[i]);
            pre=0;
            while(top&&rd[sta[top]]>rd[now])//如果栈内有元素且未找到比当前节点随机数值小的数 
            {
                update(sta[top]);//更新出栈节点 
                pre=sta[top];//记录右儿子 
                sta[top--]=0;//先出栈再top-- 
            }
            if(top)//如果栈内还有元素且找到比当前节点随机数值小的数 
                son[sta[top]][1]=now;//新入节点成为找到的节点的右儿子 
            son[now][0]=pre;//原右儿子成为新入节点的左儿子 
            sta[++top]=now;//新入节点入栈 
        }
        while(top)//栈内还有元素 
            update(sta[top--]);//全部更新 
        return sta[1];//栈底元素即为根节点 
    }
    void trash(int x)
    {
        if(!x) return;
        trashcan.push(x);
        trash(son[x][0]);
        trash(son[x][1]); 
    }
    void change(int x,int k)
    {
        val[x]=k;
        sum[x]=siz[x]*k;
        lmx[x]=rmx[x]=max(sum[x],0);//若区间内节点权值全为负数那么不如不取 
        tmx[x]=max(sum[x],val[x]);//对于当前节点sum()多了一种选择即节点自己的权值 
        lazy_revise[x]=k;//标记修改值便于以后pushdown()操作 
    }
    void flip(int x)
    {
        swap(son[x][0],son[x][1]);
        swap(lmx[x],rmx[x]);
        lazy_reverse[x]^=1;//若之前就需要翻转 那么再翻转即恢复原状态 
    }
    void pushdown(int x)//区间操作特殊函数 用于处理两个lazy() 
    {
        if(lazy_revise[x]!=INF)
        {
            if(son[x][0]) change(son[x][0],lazy_revise[x]);
            if(son[x][1]) change(son[x][1],lazy_revise[x]);
        }
        if(lazy_reverse[x])
        {
            if(son[x][0]) flip(son[x][0]);
            if(son[x][1]) flip(son[x][1]);
        }
        lazy_revise[x]=INF;
        lazy_reverse[x]=0;
    }
    void split(int now,int k,int &x,int &y)
    {
        if(!now)
        {
            x=y=0;
            return;
        }
        pushdown(now);
        if(siz[son[now][0]]>=k)//待操作区间全部在左子树 
            y=now,split(son[now][0],k,x,son[now][0]);
        else x=now,split(son[now][1],k-siz[son[now][0]]-1,son[now][1],y); 
        update(now);
    } 
    int merge(int x,int y)
    {
        if(x) pushdown(x);
        if(y) pushdown(y);
        if(!x||!y)
            return x+y;
        if(rd[x]<rd[y])
        {
            son[x][1]=merge(son[x][1],y);
            update(x);
            return x;
        }
        else
        {
            son[y][0]=merge(x,son[y][0]);
            update(y);
            return y;
        }
    }
    void insert()//插入 
    {
        int pos=read(),len=read(),x,y;
        int datas[MAXN];
        for(int i=1;i<=len;i++)
            datas[i]=read();
        int rt=build(datas,len);//把新节点先处理成一棵Treap 
        split(root,pos,x,y);//沿插入位置分成两棵Treap 
        root=merge(merge(x,rt),y);//把新Treap直接接在原Treap上 
    }
    void del()//删除 
    {
        int pos=read(),len=read(),x1,y1,x2,y2;
        split(root,pos-1,x1,y1);//把所有要修改的节点都分给y1的Treap 
        split(y1,len,x2,y2);//把所有要删除的节点都分给x2的Treap 此时x2的Treap上只有需删除的节点 
        root=merge(x1,y2);//把剩余的Treap合并 
        trash(x2);//回收节点,节省空间 
    }
    void revise()//修改 
    {
        int pos=read(),len=read(),k=read(),x1,y1,x2,y2;
        split(root,pos-1,x1,y1);//把所有要修改的节点都分给y1的Treap 
        split(y1,len,x2,y2);//把所有要修改的节点都分给x2的Treap 此时x2的Treap上只有需修改的节点 
        change(x2,k);//只需修改x2的Treap的根节点即x2 因为合并时pushdown()会完成剩余修改 
        root=merge(x1,merge(x2,y2));//把修改过的Treap同原Treap合并 
    }
    void reverse()//翻转 
    {
        int pos=read(),len=read(),x1,y1,x2,y2;
        split(root,pos-1,x1,y1);//把所有要翻转的节点都分给y1的Treap 
        split(y1,len,x2,y2);//把所有要翻转的节点都分给x2的Treap 此时x2的Treap上只有需翻转的节点 
        flip(x2);//只需将x2的Treap的根节点即x2的两个儿子翻转 因为合并时pushdown()会完成剩余翻转 
        root=merge(x1,merge(x2,y2));//把翻转过的Treap同原Treap合并 
    }
    void get_sum()//区间权值和 
    {
        int pos=read(),len=read(),x1,y1,x2,y2;
        split(root,pos-1,x1,y1);//把所求区间分给y1的Treap 
        split(y1,len,x2,y2);//把所求区间分给x2的Treap 此时x2的Treap即为所求区间 
        printf("%d
    ",sum[x2]); 
        root=merge(x1,merge(x2,y2));//把所求区间同原Treap合并 
    }
    void sub_sum()//求整个序列内的最大子段和 
    {
        printf("%d
    ",tmx[root]); 
    }
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        root=build(a,n);
        for(int i=1;i<=m;i++)
        {
            char s[10];
            scanf("%s",s);
            if(s[0]=='I') insert();
            if(s[0]=='D') del();
            if(s[0]=='M'&&s[2]=='K') revise();
            if(s[0]=='R') reverse();
            if(s[0]=='G') get_sum();
            if(s[0]=='M'&&s[2]=='X') sub_sum();
        }
         return 0;
    }
     
  • 相关阅读:
    java 内部类
    webservice restful rpc
    linux 修改文件权限chmod
    java ThreadLocal的理解
    转:Eclipse常用开发插件
    Eclipse安装插件支持jQuery智能提示
    转:VS2008 vs2010中JQUERY智能提醒
    jquery ui和jquery easy ui的区别
    线程池
    java连接数据库URL
  • 原文地址:https://www.cnblogs.com/water-radish/p/9280881.html
Copyright © 2020-2023  润新知