ZJOI 2006 书架

题意

请你维护一个长度为\(n\)的排列,要求支持以下操作

  • 把权值为\(v\)的元素放到序列的首端
  • 把权值为\(v\)的元素放到序列的尾端
  • 把权值为\(v\)的元素和它前一个或后一个元素进行交换
  • 询问权值为\(v\)的元素之前有多少个元素
  • 询问第\(k\)个元素的权值

解法

可以用无旋Treap来维护区间

和Splay一样,Treap可以用来维护区间是由于它的下标的中序遍历是整个序列的映射

我们建立由权值到位置的映射pos,我们就能直接找到权值为\(v\)的元素在Treap上对应的结点

除了无旋Treap的基础操作外,我们发现由于维护的现在是下标而不是权值,如果要求序列中的第\(k\)个数我们并不能用传统的查询第\(k\)大的方式进行

我们维护每个节点的父亲,对于权值为\(v\)的元素,我们暴力向上跳父亲,统计所有比当前数小的元素个数

这样我们就得到了权值为\(v\)的元素在排列中的位置

注意,由于是维护序列,这里的Split操作是按照树的大小进行分裂的,若我们对整颗树进行参数为\(k\)的Split操作,这时候我们得到的大小为\(k\)的子树即为排列的前\(k\)个数

代码

#include <ctime>
#include <cstdio>
#include <cstdlib>

using namespace std;

const int N = 1e5 + 10;

int read();

int n, m;
int pos[N];

char op[10];

struct FHQ_Treap {
#define ls(x) t[x].ch[0]
#define rs(x) t[x].ch[1]
#define fa(x) t[x].fa
    
    int cnt;
    int root, a, b, c, d;   
        
    struct node {
        int siz, val, rnd, fa;
        int ch[2];
        node() { ch[0] = ch[1] = 0; }       
    } t[N];
    
    FHQ_Treap() : cnt(0), root(0) {}
    
    int newnode(int v) {
        ++cnt;
        t[cnt].siz = 1, t[cnt].val = v, t[cnt].rnd = rand() << 15 | rand();
        return cnt;
    }
    
    void update(int x) {
        t[x].siz = t[ls(x)].siz + t[rs(x)].siz + 1;
        fa(ls(x)) = fa(rs(x)) = x;
    }
    
    void split(int x, int k, int& lt, int& rt) {
        if (!x)
            return lt = rt = 0, void();
        if (k <= t[ls(x)].siz)
            rt = x, split(ls(x), k, lt, ls(x));
        else
            lt = x, split(rs(x), k - t[ls(x)].siz - 1, rs(x), rt);
        update(x);
    }
    
    int merge(int x, int y) {
        if (!x || !y)
            return x | y;
        if (t[x].rnd < t[y].rnd) {
            rs(x) = merge(rs(x), y);
            return update(x), x;
        } else {
            ls(y) = merge(x, ls(y));
            return update(y), y;
        }
    }
    
    int kth(int k) {
        split(root, k - 1, a, b);
        split(b, 1, b, c);
        int res = t[b].val;
        root = merge(a, merge(b, c));
        return res;
    }
    
    int find(int x) {
        int res = t[ls(x)].siz + 1;
        for (; x; x = fa(x))
            if (x == rs(fa(x)))  res += t[ls(fa(x))].siz + 1;
        return res;
    }
    
    void insert(int v) {
        root = merge(root, newnode(v)); 
    }
    
    void top(int x) {
        split(root, x - 1, a, b);
        split(b, 1, b, c);
        root = merge(b, merge(a, c));
    }
    
    void bottom(int x) {
        split(root, x - 1, a, b);
        split(b, 1, b, c);
        root = merge(merge(a, c), b);   
    }
    
    void change(int x, int t) {
        if (!t)
            return;
        t = (~t) ? 1 : 2;
        split(root, x - t, a, b);
        split(b, 2, b, c);
        split(b, 1, b, d);
        root = merge(a, merge(merge(d, b), c));
    }
    
    void debug(int x) {
        printf("%d ls: %d rs: %d sz: %d\n", x, ls(x), rs(x), t[x].siz); 
        if (ls(x)) debug(ls(x));
        if (rs(x)) debug(rs(x));
    }
    
#undef ls
#undef rs
#undef fa
} tr;

int main() {
    
    srand(time(NULL));
    
    n = read(), m = read();
    
    for (int i = 1; i <= n; ++i) {
        int v = read();
        tr.insert(v);
        pos[v] = i;
    }
    
//  tr.debug(tr.root);
    
    for (int i = 1; i <= m; ++i) {
        
        scanf("%s", op + 1);
        
        int S = read(); 
        int rnk = tr.find(pos[S]);
        
        switch (op[1]) {
        case 'T':
            tr.top(rnk);  
            break;
        case 'B':
            tr.bottom(rnk); 
            break;
        case 'I':
            tr.change(rnk, read()); 
            break;
        case 'A':
            printf("%d\n", rnk - 1);
            break;
        case 'Q':
            printf("%d\n", tr.kth(S));
            break;  
        }
    }
        
    return 0;
}

int read() {
    int x = 0, f = 1, c = getchar();
    while (c < '0' || c > '9')    c == '-' ? f = -1, c = getchar() : c = getchar();
    while (c >= '0' && c <= '9')  x = x * 10 + c - 48, c = getchar();
    return x * f;   
}

转载于:https://www.cnblogs.com/VeniVidiVici/p/11593818.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值