ZOJ 2112 Dynamic Rankings 线段树套平衡树

题目:

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2112

题意:

给出一个长度为n的整数数组,有以下两种操作: Q i j k意为求区间[i,j]内的第k小数,C i t意为把第i个数换成t

思路:

一直觉得树套树高大上,今天学着写了一波。。。建立一颗线段树,线段树中每个节点再对应一棵平衡树,用来维护节点所管辖的区间,用平衡树可以在log(n)的时间内确定一个值的名次。于是可以二分枚举第k大值,然后在去线段树中查询这个值的名次,就可以了,大致就是这样,平衡树用的是treap

#include <bits/stdc++.h>

const int N = 50000 + 10, INF = 0x3f3f3f3f;

int tot;
int a[N];

struct seg_node
{
    int l, r, root;
}g[N*4];
struct treap_node
{
    int val, pri, son[2];
    int sz, num;
    void init(int _val, int _pri, int _sz, int _num)
    {
        val = _val, pri = _pri, sz = _sz, num = _num;
        son[0] = son[1] = 0;
    }
}tr[N*20];
void treap_init()
{
    tot = 0;
    tr[0].init(0, 0, 0, 0);
}
void treap_update(int x)
{
    tr[x].sz = tr[tr[x].son[0]].sz + tr[tr[x].son[1]].sz + tr[x].num;
}
void treap_rotate(int &x, int p)
{
    int y = tr[x].son[!p];
    tr[x].son[!p] = tr[y].son[p];
    tr[y].son[p] = x;
    treap_update(x), treap_update(y);
    x = y;
}
void treap_insert(int &x, int val)
{
    if(! x) tr[x = ++tot].init(val, rand(), 1, 1);
    else
    {
        tr[x].sz++;
        if(tr[x].val == val) tr[x].num++;
        else
        {
            int p = val > tr[x].val;
            treap_insert(tr[x].son[p], val);
            if(tr[x].pri < tr[tr[x].son[p]].pri) treap_rotate(x, !p);
        }
    }
}
void treap_delete_node(int &x, int val)
{
    if(tr[x].val == val)
    {
        if(tr[x].num > 1) tr[x].sz--, tr[x].num--;
        else if(tr[x].son[0] && tr[x].son[1])
        {


            int p = tr[tr[x].son[0]].pri > tr[tr[x].son[1]].pri;
            treap_rotate(x, p);
            treap_delete_node(x, val);
        }
        else x = tr[x].son[0] + tr[x].son[1];
    }
    else
    {
        tr[x].sz--;
        int p = val > tr[x].val;
        treap_delete_node(tr[x].son[p], val);
    }
}
int treap_get_rank(int x, int val)
{
    if(! x) return 0;
    if(val > tr[x].val) return tr[x].num + tr[tr[x].son[0]].sz + treap_get_rank(tr[x].son[1], val);
    else if(val < tr[x].val) return treap_get_rank(tr[x].son[0], val);
    else return tr[tr[x].son[0]].sz + tr[x].num;
}
void seg_build(int l, int r, int k)
{
    g[k].l = l, g[k].r = r, g[k].root = 0;
    //treap_insert(g[k].root, a[l]);
    if(l == r) return;
    int mid = (l + r) >> 1;
    seg_build(l, mid, k << 1);
    seg_build(mid + 1, r, k << 1|1);
}
void seg_init(int x, int k, int val)
{
    treap_insert(g[k].root, val);
    if(g[k].l == g[k].r) return;
    int mid = (g[k].l + g[k].r) >> 1;
    if(x <= mid) seg_init(x, k << 1, val);
    else seg_init(x, k << 1|1, val);
}
void seg_update(int x, int tval, int val, int k)
{
    treap_delete_node(g[k].root, tval);
    treap_insert(g[k].root, val);
    if(g[k].l == g[k].r) return;
    int mid = (g[k].l + g[k].r) >> 1;
    if(x <= mid) seg_update(x, tval, val, k << 1);
    else seg_update(x, tval, val, k << 1|1);
}
int seg_query(int l, int r, int val, int k)
{
    if(l <= g[k].l && g[k].r <= r)
    {
        return treap_get_rank(g[k].root, val);
    }
    int mid = (g[k].l + g[k].r) >> 1;
    int ans = 0;
    if(l <= mid) ans += seg_query(l, r, val, k << 1);
    if(r > mid) ans += seg_query(l, r, val, k << 1|1);
    return ans;
}
int main()
{
    int t, n, m;
    scanf("%d", &t);
    while(t--)
    {
        treap_init();
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        seg_build(1, n, 1);
        for(int i = 1; i <= n; i++) seg_init(i, 1, a[i]);//把所有值依次插入到线段树对应节点的平衡树中
        char ch;
        int x, y, z;
        for(int i = 1; i <= m; i++)
        {
            scanf(" %c", &ch);
            if(ch == 'Q')
            {
                scanf("%d%d%d", &x, &y, &z);
                int l = -1e9, r = 1e9, res;
                while(l <= r)
                {
                    int mid = (l + r) >> 1;
                    int k = seg_query(x, y, mid, 1);
                    if(k >= z) res = mid, r = mid - 1;
                    else l = mid + 1;
                }
                printf("%d\n", res);
            }
            else
            {
                scanf("%d%d", &x, &y);
                seg_update(x, a[x], y, 1);
                a[x] = y;
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值