Luogu P3835 【模板】可持久化平衡树

P3835 【模板】可持久化平衡树

题意

题目背景

本题为题目普通平衡树的可持久化加强版。

题目描述

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作(对于各个以往的历史版本):

  1. 插入\(x\)数
  2. 删除\(x\)数(若有多个相同的数,因只删除一个,如果没有请忽略该操作)
  3. 查询\(x\)数的排名(排名定义为比当前数小的数的个数\(+1\)。若有多个相同的数,因输出最小的排名)
  4. 查询排名为x的数
  5. 求\(x\)的前驱(前驱定义为小于\(x\),且最大的数,如不存在输出\(-2147483647\))
  6. 求\(x\)的后继(后继定义为大于\(x\),且最小的数,如不存在输出\(2147483647\))

和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本。(操作\(3,4,5,6\)即保持原版本无变化)

每个版本的编号即为操作的序号(版本\(0\)即为初始状态,空树)

输入输出格式

输入格式:

第一行包含一个正整数\(N\),表示操作的总数。

接下来每行包含三个整数,第\(i\)行记为\(v_i,opt_i,x_i\)。

\(v_i\)表示基于的过去版本号\((0\leq v_i<i)\),\(opt_i\)表示操作的序号\((1\leq opt\leq 6)\),\(x_i\)表示参与操作的数值。

输出格式:

每行包含一个正整数,依次为各个\(3,4,5,6\)操作所对应的答案

输入输出样例

输入样例#1:

10
0 1 9
1 1 3
1 1 10
2 4 2
3 3 9
3 1 2
6 4 1
6 2 9
8 6 3
4 5 8

输出样例#1:

9
1
2
10
3

说明

数据范围:

对于\(28%\)的数据满足:\(1\leq n\leq 10\)

对于\(44%\)的数据满足:\(1\leq n\leq 2\cdot {10}^2\)

对于\(60%\)的数据满足:\(1\leq n\leq 3\cdot {10}^3\)

对于\(84%\)的数据满足:\(1\leq n\leq {10}^5\)

对于\(92%\)的数据满足:\(1\leq n\leq 2\cdot {10}^5\)

对于\(100%\)的数据满足:\(1\leq n\leq 5\cdot {10}^5\)

经实测,正常常数的可持久化平衡树均可通过,请各位放心

样例说明:

共\(10\)次操作,\(11\)个版本,各版本的状况依次是:

  1. \([]\)
  2. \([9]\)
  3. \([3,9]\)
  4. \([9,10]\)
  5. \([3,9]\)
  6. \([9,10]\)
  7. \([2,9,10]\)
  8. \([2,9,10]\)
  9. \([2,10]\)
  10. \([2,10]\)
  11. \([3,9]\)

思路

\(fhq\ Treap\)天下第一! --Uranus

在可持久化的状况下,\(fhq\ Treap\)的优越性被发挥得淋漓尽致。总体函数完全没有变化,只是多加了几条新建点的语句:

int merge(int x,int y)
{
    if(!x||!y) return x+y;
    if(rnd(x)>rnd(y))
    {
        int p=++cnt;node[p]=node[x];//new
        rs(p)=merge(rs(p),y);
        update(p);
        return p;
    }
    else
    {
        int p=++cnt;node[p]=node[y];//new
        ls(p)=merge(x,ls(p));
        update(p);
        return p;
    }
}
void split(int now,int k,int &x,int &y)
{
    if(!now) x=y=0;
    else
    {
        if(val(now)<=k)
        {
            x=++cnt;node[x]=node[now];//new
            split(rs(x),k,rs(x),y);
            update(x);
        }
        else
        {
            y=++cnt;node[y]=node[now];//new
            split(ls(y),k,x,ls(y));
            update(y);
        }
    }
}

上面代码标了new的就是新语句。

其他的操作完全相同,访问历史版本的操作也只需要对于每一个版本根不相同就可以了。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int INF=2147483647;
const int MAXN=5e5+5;
int n,cnt,rt[MAXN];
struct fhq_Treap
{
    int sz,rnd,val;
    int ls,rs;
    #define sz(a) node[a].sz
    #define rnd(a) node[a].rnd
    #define val(a) node[a].val
    #define ls(a) node[a].ls
    #define rs(a) node[a].rs
}node[MAXN<<6];
int read()
{
    int re=0;bool f=true;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=false;ch=getchar();}
    while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
    return f?re:-re;
}
int newnode(int k)
{
    sz(++cnt)=1;
    rnd(cnt)=rand();
    val(cnt)=k;
    return cnt;
}
void update(int p){sz(p)=sz(ls(p))+sz(rs(p))+1;}
int merge(int x,int y)
{
    if(!x||!y) return x+y;
    if(rnd(x)>rnd(y))
    {
        int p=++cnt;node[p]=node[x];
        rs(p)=merge(rs(p),y);
        update(p);
        return p;
    }
    else
    {
        int p=++cnt;node[p]=node[y];
        ls(p)=merge(x,ls(p));
        update(p);
        return p;
    }
}
void split(int now,int k,int &x,int &y)
{
    if(!now) x=y=0;
    else
    {
        if(val(now)<=k)
        {
            x=++cnt;node[x]=node[now];
            split(rs(x),k,rs(x),y);
            update(x);
        }
        else
        {
            y=++cnt;node[y]=node[now];
            split(ls(y),k,x,ls(y));
            update(y);
        }
    }
}
int kth(int now,int k)
{
    if(k==sz(ls(now))+1) return val(now);
    else if(k<=sz(ls(now))) return kth(ls(now),k);
    else return kth(rs(now),k-sz(ls(now))-1);
}
int main()
{
    srand(time(0));
    n=read();
    for(int i=1;i<=n;i++)
    {
        int ver=read(),opt=read(),k=read();rt[i]=rt[ver];
        if(opt==1)
        {
            int x,y;
            split(rt[i],k,x,y);
            rt[i]=merge(merge(x,newnode(k)),y);
        }
        else if(opt==2)
        {
            int x,y,z;
            split(rt[i],k,x,z);
            split(x,k-1,x,y);
            y=merge(ls(y),rs(y));
            rt[i]=merge(merge(x,y),z);
        }
        else if(opt==3)
        {
            int x,y;
            split(rt[i],k-1,x,y);
            printf("%d\n",sz(x)+1);
            rt[i]=merge(x,y);
        }
        else if(opt==4) printf("%d\n",kth(rt[i],k));
        else if(opt==5)
        {
            int x,y;
            split(rt[i],k-1,x,y);
            if(!x) printf("%d\n",-INF);
            else printf("%d\n",kth(x,sz(x)));
            rt[i]=merge(x,y);
        }
        else if(opt==6)
        {
            int x,y;
            split(rt[i],k,x,y);
            if(!y) printf("%d\n",INF);
            else printf("%d\n",kth(y,1));
            rt[i]=merge(x,y);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/coder-Uranus/p/9887157.html

时间: 2024-11-05 12:00:37

Luogu P3835 【模板】可持久化平衡树的相关文章

luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树)(主席树)

luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树) 题目 #include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<cstring> #include<iomanip> #include<algorithm> #include<ctime> #include<queue> #inc

洛谷P3835 【模板】可持久化平衡树

题目背景 本题为题目 普通平衡树 的可持久化加强版. 数据已经经过强化 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作(对于各个以往的历史版本): 插入x数 删除x数(若有多个相同的数,因只删除一个,如果没有请忽略该操作) 查询x数的排名(排名定义为比当前数小的数的个数+1.若有多个相同的数,因输出最小的排名) 查询排名为x的数 求x的前驱(前驱定义为小于x,且最大的数,如不存在输出-2147483647) 求x的后继(后继定义为大于x,且最小的数,如不存在

数组splay ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) #include <cstdio> #define Max 100005 #define Inline __attri\ bute__( ( optimize( "-O2" ) ) ) Inline void read (int &now) { now = 0; register char word = getchar (); bool temp = false; while (wor

替罪羊树 ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) 闲的没事,把各种平衡树都写写 比较比较... 下面是替罪羊树 #include <cstdio> #include <vector> #define Max_ 100010 #define Inline __attri\ bute__( ( optimize( "-O2" ) ) ) Inline void read (int &now) { register char w

红黑树 ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) 近几天闲来无事...就把各种平衡树都写了一下... 下面是红黑树(Red Black Tree) #include <cstdio> #define Max 100001 #define Red true #define Black false #define Inline __attri\ bute__( ( optimize( "-O2" ) ) ) Inline void read (i

【模板】可持久化平衡树

题目背景 本题为题目 普通平衡树 的可持久化加强版. 数据已经经过强化 感谢@Kelin 提供的一组hack数据 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作(对于各个以往的历史版本): 插入x数 删除x数(若有多个相同的数,因只删除一个,如果没有请忽略该操作) 查询x数的排名(排名定义为比当前数小的数的个数+1.若有多个相同的数,因输出最小的排名) 查询排名为x的数 求x的前驱(前驱定义为小于x,且最大的数,如不存在输出-2147483647) 求x的后

P3369 【模板】普通平衡树(Treap/SBT)

二次联通门 : P3369 [模板]普通平衡树(Treap/SBT) /* luogu P3369 [模板]普通平衡树(Treap/SBT) splay 模板 支持插入x数 删除x数(若有多个相同的数,因只删除一个) 查询x数的排名(若有多个相同的数,因输出最小的排名) 查询排名为x的数 求x的前驱(前驱定义为小于x,且最大的数) 求x的后继(后继定义为大于x,且最小的数) */ #include <cstdio> #define Max 200005 void read (int &

可持久化平衡树详解及实现方法分析

目录 前置要求 核心思想 实现方法比较:指针 与 数组模拟指针 Merge 是否应该新建节点 参考程序 前置要求 带旋转的平衡树会改变祖先关系,这令可持久化变得困难.所以需要使用非旋的平衡树,如非旋treap.本文以非旋treap为例. 核心思想 可持久化的数据结构,其核心都是不改变历史的信息.当需要对信息进行修改的时候就新开一个节点,继承历史信息,然后再进行修改. 对于非旋treap来说,主要是对 Split 和 Merge 两个操作进行可持久化.剩下的操作不会对数据产生影响.而考虑到非旋tr

P3391 【模板】文艺平衡树(Splay)

题目背景 这是一道经典的Splay模板题--文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入输出格式 输入格式: 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2, \cdots n-1,n)(1,2,?n?1,n) m表示翻转操作次数 接下来m行每行两个数 [l,r][l,r] 数据保证 1 \leq l \leq r