☆ [ZJOI2006] 书架 「平衡树维护数列」

题目类型:平衡树

传送门:>Here<

题意:要求维护一个数列,支持:将某个元素置顶或置底,交换某元素与其前驱或后继的位置,查询编号为\(S\)的元素的排名,查询排名第\(k\)的元素编号

解题思路

可以说是平衡树维护数列的入门题。当平衡树在维护数列时,关键字是在数列中的排名。因此中序遍历即为当前数列。注意在平衡树维护数列中,会涉及到两个编号。一个编号是这个节点在平衡树内的编号,一般外界是不会直接访问的。另一个是题目赋予的编号,代表这个位置数列上对应的值。外部编号即为一个附加值。当我们需要找到外部编号为\(S\)的元素,好像比较麻烦。此时,我们需要多开一个数组\(pos\),令\(pos[idx[o]]=o\),相当于是\(idx\)数组的逆运算。在知道一个外部编号时,能够迅速找到其所对应的节点编号。然后就可以对其进行直接操作了

至于前驱后继交换,我们只需要交换这两个节点所对应的外部编号以及更新\(pos\),没有必要交换平衡树内的节点。

Code

/*By DennyQi 2018*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define  r  read()
using namespace std;
typedef long long ll;
const int MAXN = 80010;
const int INF = 1061109567;
inline int Max(const int a, const int b){ return (a > b) ? a : b; }
inline int Min(const int a, const int b){ return (a < b) ? a : b; }
inline int read(){
    int x = 0; int w = 1; register char c = getchar();
    for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
    if(c == '-') w = -1, c = getchar();
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
}
int N,M,S,T;
char opt[10];
int idx[MAXN],pos[MAXN];
struct Splay{
    int fa[MAXN],ch[MAXN][2],size[MAXN],num_node,rt;
    inline void update(int x){
        size[x] = 1;
        if(ch[x][0]) size[x] += size[ch[x][0]];
        if(ch[x][1]) size[x] += size[ch[x][1]];
    }
    inline bool rson(int f, int x){
        return ch[f][1] == x;
    }
    inline void rotate(int x){
        int f = fa[x], gf = fa[f];
        bool p = rson(f, x), q = !p;
        if(!gf) rt = x; else ch[gf][rson(gf,f)] = x;
        fa[x] = gf;
        ch[f][p] = ch[x][q], fa[ch[x][q]] = f;
        ch[x][q] = f, fa[f] = x;
        update(f), update(x);
    }
    inline void splay(int x, int target){
        int f,gf;
        while(fa[x] != target){
            f = fa[x], gf = fa[f];
            if(gf == target){
                rotate(x);
                break;
            }
            if(rson(gf,f) ^ rson(f,x)) rotate(x); else rotate(f);
            rotate(x);
        }
    }
    inline void init(int id){
        if(!rt){
            rt = ++num_node;
            idx[num_node] = id;
            pos[id] = num_node;
            size[num_node] = 1;
            return;
        }
        int pre_rt = rt;
        rt = ++num_node;
        ch[rt][0] = pre_rt;
        fa[pre_rt] = rt;
        size[rt] = size[pre_rt] + 1;
        idx[rt] = id;
        pos[id] = rt;
    }
    inline void top(int s){
        int o = pos[s];
        splay(o, 0);
        if(!ch[rt][0] && !ch[rt][1]) return;
        int p = ch[rt][1];
        if(!p){
            ch[rt][1] = ch[rt][0];
            ch[rt][0] = 0;
            splay(ch[rt][1], 0);
            return;
        }
        while(ch[p][0]) p = ch[p][0];
        splay(p, rt);
        ch[p][0] = ch[rt][0];
        fa[ch[rt][0]] = p;
        ch[rt][0] = 0;
        update(p), update(rt);
    }
    inline void bottom(int s){
        int o = pos[s];
        splay(o, 0);
        if(!ch[rt][0] && !ch[rt][1]) return;
        int p = ch[rt][0];
        if(!p){
            ch[rt][0] = ch[rt][1];
            ch[rt][1] = 0;
            splay(ch[rt][0], 0);
            return;
        }
        while(ch[p][1]) p = ch[p][1];
        splay(p, rt);
        ch[p][1] = ch[rt][1];
        fa[ch[rt][1]] = p;
        ch[rt][1] = 0;
        update(p), update(rt);
    }
    inline int ask(int s){
        int o = pos[s];
        splay(o, 0);
        return size[ch[rt][0]];
    }
    inline int query(int k){
        int o = rt;
        while(o){
            if(size[ch[o][0]] >= k){
                o = ch[o][0];
            }
            else if(size[ch[o][0]] + 1 < k){
                k -= size[ch[o][0]] + 1;
                o = ch[o][1];
            }
            else{
                return idx[o];
            }
        }
        return -1;
    }
    inline void insert(int S, int T){
        if(T == 0) return;
        splay(pos[S], 0);
        if(T == -1){
            int o = ch[rt][0];
            while(ch[o][1]) o = ch[o][1];
            splay(o, rt);
            swap(idx[rt], idx[o]);
            pos[idx[rt]] = rt, pos[idx[o]] = o;
            update(o), update(rt);
        }
        if(T == 1){
            int o = ch[rt][1];
            while(ch[o][0]) o = ch[o][0];
            splay(o, rt);
            swap(idx[rt], idx[o]);
            pos[idx[rt]] = rt, pos[idx[o]] = o;
            update(o), update(rt);
        }
    }
}qxz;
int main(){
    scanf("%d %d", &N, &M);
    for(int i = 1; i <= N; ++i){
        scanf("%d", &S);
        qxz.init(S);
    }
    while(M--){
        scanf("%s %d", opt, &S);
        if(opt[0] == 'T'){
            qxz.top(S);
        }
        if(opt[0] == 'B'){
            qxz.bottom(S);
        }
        if(opt[0] == 'A'){
            printf("%d\n", qxz.ask(S));
        }
        if(opt[0] == 'Q'){
            printf("%d\n", qxz.query(S));
        }
        if(opt[0] == 'I'){
            scanf("%d", &T);
            qxz.insert(S,T);
        }
    }
}

原文地址:https://www.cnblogs.com/qixingzhi/p/9654947.html

时间: 2024-10-05 05:55:20

☆ [ZJOI2006] 书架 「平衡树维护数列」的相关文章

数据结构(Splay平衡树):COGS 339. [NOI2005] 维护数列

339. [NOI2005] 维护数列 ★★★★☆   输入文件:seq2005.in   输出文件:seq2005.out   简单对比 时间限制:3 s   内存限制:256 MB [问题描述] 请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格) 操作编号 输入文件中的格式 说明 1.  插入 INSERT_posi_tot_c1_c2_..._ctot 在当前数列的第 posi 个数字后插入 tot 个数字:c1, c2,

「CSPS 2019 十一」 贪心

一般来说,如果题目需要求一个最优解或者最小(大)花费之类的,而且除了暴力之外想不到什么好方法,那么就可能需要用贪心. 通常地,我们猜想一些步骤能不能直接使用贪心,然后再去证明这个贪心是对的. 有时候可能要多想几种贪心才能找到正确的那一种. New Year Snowmen Description 要堆起一个雪人,需要三个不同大小的雪球.现在有 \(n\) 个给定大小的雪球,问最多能堆起多少个雪人,并输出方案. Solution 每次用数量最多的三个雪球是最优的.可以用一个单调队列,每次取出最大的

LibreOJ #517. 「LibreOJ β Round #2」计算几何瞎暴力

二次联通门 : LibreOJ #517. 「LibreOJ β Round #2」计算几何瞎暴力 /* LibreOJ #517. 「LibreOJ β Round #2」计算几何瞎暴力 叫做计算几何 实则毒瘤数据结构 看到xor后 考虑Trie树 Trie树的每一个节点保存的是以当前子树中每个二进制位的个数 给Trie打一个全局xor标记,如果标记这一位是1,就交换它的两个儿子 另外维护一个前缀和 前缀和存的是没sort过的值的和 Trie维护的是sort之后的值 1操作直接在前缀和后加就好

loj #535. 「LibreOJ Round #6」花火 树状数组求逆序对+主席树二维数点+整体二分

$ \color{#0066ff}{ 题目描述 }$ 「Hanabi, hanabi--」 一听说祭典上没有烟火,Karen 一脸沮丧. 「有的哦-- 虽然比不上大型烟花就是了.」 还好 Shinobu 早有准备,Alice.Ayaya.Karen.Shinobu.Yoko 五人又能继续愉快地玩耍啦! 「噢--!不是有放上天的烟花嘛!」Karen 兴奋地喊道. 「啊等等--」Yoko 惊呼.Karen 手持点燃引信的烟花,「嗯??」 Yoko 最希望见到的是排列优美的烟火,当然不会放过这个机会-

架构设计之「 微服务入门 」

微服务这几年不可谓不火,很多技术团队都开始在自己的项目上引入了微服务.一方面这些团队确实很好的推动了微服务的应用和发展,另一方面也可以看到一些盲目追技术热点的行为所带来的危害,比如很多中小团队对微服务的基础知识只是做了很浅显的了解就开始盲目的推动微服务的实施,最后导致了项目的失败. 微服务要想做好是一个非常复杂的架构,今天就先只聊一聊微服务的一些基础架构,算是入门篇. 一.什么是「 微服务 」? 「 微服务 」由 Martin Fowler 提出,它是指一种软件架构风格.一个大型的系统可以由多个

「清华集训2014」主旋律

「清华集训2014」主旋律 这个题好难难啊,我想了一个小时连50分都不会,只能去摸周指导了. 我们试图直接爆算集合 \(S\) 的非强连通导出子图数量,考虑将这个导出子图的所有强连通分量缩点后,一定是一个点数 \(\geq 2\) 的 \(\text{DAG}\) .即缩完以后至少要有一个入度为 \(0\) 的点,这个条件充分性显然,必要性考虑如果不存在入度为 \(0\) 的点,那么一定存在一个环,说明并没有将所有强连通分量缩掉.然后我们枚举入度为 \(0\) 的点集 \(T\) ,记 \(F(

LeetCode 5248. 统计「优美子数组」

地址 https://www.acwing.com/solution/leetcode/content/5801/ 题目描述给你一个整数数组 nums 和一个整数 k. 如果某个子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」. 请返回这个数组中「优美子数组」的数目. 示例 1: 输入:nums = [1,1,2,1,1], k = 3 输出:2 解释:包含 3 个奇数的子数组是 [1,1,2,1] 和 [1,2,1,1] . 示例 2: 输入:nums = [2,4,6],

LibreOJ #515. 「LibreOJ β Round #2」贪心只能过样例

二次联通门 : LibreOJ #515. 「LibreOJ β Round #2」贪心只能过样例 /* LibreOJ #515. 「LibreOJ β Round #2」贪心只能过样例 很显然 贪心方程哦不 dp方程为 f[i][j]=f[i-1][j-k*k] 但是这样的话复杂度就是O(N ^ 5) 那么就用bitset优化一下 就ok了 */ #include <iostream> #include <cstdio> #include <bitset> void

Linux 小知识翻译 - 「编译器和解释器」

这次聊聊「编译器和解释器」. 编程语言中,有以C为代表的编译型语言和以Perl为代表的解释型语言.不管是哪种,程序都是以人类能够理解的形式记录的,这种形式计算机是无法理解的. 因此,才会有编译器和解释器. 对于编译型语言,是使用编译器将人类可读的代码转换为机器能够理解的「机器语言」文件,然后通过执行这个「机器语言」文件来实现程序的执行. 另一方面,对于解释型语言,是使用解释器将人类可读的代码逐行解释,一边解释一边执行这个程序.(这里的解释是将代码解释成机器语言,让计算机能够理解) 甚至有的语言既