平衡樹 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\n",sum[x2]); 
    root=merge(x1,merge(x2,y2));//把所求區間同原Treap合並 
}
void sub_sum()//求整個序列內的最大子段和 
{
    printf("%d\n",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;
}
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM