平衡树 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\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;
}

原文地址:https://www.cnblogs.com/water-radish/p/9280881.html

时间: 2024-10-16 15:20:00

平衡树 fhqTreap 区间操作的相关文章

hdu 4348 To the moon(主席树区间操作)

题目链接:hdu 4348 To the moon 题意: 给你n个数,有m个操作. 1.给区间[l,r]的所有数+d,并且时间戳+1 2.询问当前时间戳的区间和. 3.询问过去时间戳t的区间和. 4.退回到时间戳t. 题解: 直接上主席树. 不过区间操作的时候push_down空间似乎不是那么够用. 所有把push_down这个操作去掉. 用一个标记记录当前这个区间的累加. 询问的时候就将这个累加传下去.(具体看代码) 最后还有退回状态t的时候可以把cnt=root[t+1], 因为后面的内存

HDU 4578 线段树区间更新(确定区间操作的优先级)

HDU 4578 线段树区间更新 操作有: 区间所有数add(c) 区间所有数mul(c) 区间所有数set(c) 查询有: 区间所有数的p次方和(p>= 1 && p <= 3) 关键是区间更新的三种操作的优先级的确定清楚set>mul>add 关键是:down和update中对区间的更新操作是一回事,可以写成函数方便编程 //#pragma warning (disable: 4786) //#pragma comment (linker, "/STA

平衡树 fhqTreap

//Treap fhq版(不旋转) //所有操作依靠split()(分离)和merge()(合并)完成 //可支持区间操作和可持久化 比普通Treap更通用 //所有Treap中序遍历均为递增序列 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<

L - Vases and Flowers - hdu 4614(区间操作)

题意:有两种操作,第一种从A开始插花,如果有花就跳到下一个,然后输出最后一个花瓶的编号,如果花瓶不够把多余的花丢掉.操作2把区间清空 分析:很明显的线段树操作,就是插花的时候麻烦一下,需要先找出来他剩余的花瓶数,要不没办法更新. ******************************************************************* #include<algorithm>#include<stdio.h>using namespace std; #de

Codeforces 719E [斐波那契区间操作][矩阵快速幂][线段树区间更新]

/* 题意:给定一个长度为n的序列a. 两种操作: 1.给定区间l r 加上某个数x. 2.查询区间l r sigma(fib(ai)) fib代表斐波那契数列. 思路: 1.矩阵操作,由矩阵快速幂求一个fib数根据矩阵的乘法结合率,A*C+B*C=(A+B)*C; 这样可以通过线段树维护某个区间2*1矩阵的和. 2.时限卡的紧...用我的矩阵乘法板子TLE了.所以把板子里边的三重循环改成手工公式... 3.注意(a+b)%mod.这种,改成if(a+b>=mod)a+b-mod这种形式时间几乎

2014多校10(1003)hdu4973(简单线段树区间操作)

A simple simulation problem. Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 679    Accepted Submission(s): 269 Problem Description There are n types of cells in the lab, numbered from 1 to n.

poj-3468 区间操作

http://poj.org/problem?id=3468 区间求和操作 ,一个区间加操作. #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <cstdlib> #include <algorithm> #include <vector> #include <set> #include &l

P3369 【模板】普通平衡树FHQtreap

P3369 [模板]普通平衡树(Treap/SBT) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入x数 删除x数(若有多个相同的数,因只删除一个) 查询x数的排名(排名定义为比当前数小的数的个数+1.若有多个相同的数,因输出最小的排名) 查询排名为x的数 求x的前驱(前驱定义为小于x,且最大的数) 求x的后继(后继定义为大于x,且最小的数) 输入输出格式 输入格式: 第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(

Splay 区间操作(二)

首先基本操作如下: 删除第rank个点 void Remove(int id){//删除第rank个点 rank++; int x = find(root, rank - 1); splay(x, 0); x = find(root, rank + 1); splay(x, root); ch[ch[root][1]][0] = 0; pushup(ch[root][1]),pushup(root); } 删除编号为id的点 void Remove(int id){//删除编号为id的点 spl