平衡树 fhqTreap 区间操作

Posted water-radish

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了平衡树 fhqTreap 区间操作相关的知识,希望对你有一定的参考价值。

//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;
}

 

以上是关于平衡树 fhqTreap 区间操作的主要内容,如果未能解决你的问题,请参考以下文章

平衡树 fhqTreap

fhqtreap的学习笔记

算法fhqtreap初探

[模板]fhqTreap

fhqtreap入门

P3369 模板普通平衡树 FHQtreap