[Luogu P3380] [BZOJ 3196]【模板】二逼平衡树(树套树)

5 篇文章 0 订阅
2 篇文章 0 订阅
洛谷传送门(数据水)
BZOJ传送门

题目描述(洛谷)

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:

  • 查询k在区间内的排名
  • 查询区间内排名为k的值
  • 修改某一位值上的数值
  • 查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)
  • 查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647)

注意上面两条要求和tyvj或者bzoj不一样,请注意(说明数据是rand的…)

输入输出格式

输入格式:

第一行两个数 n,m n , m 表示长度为 n n 的有序序列和m个操作

第二行有 n n 个数,表示有序序列

下面有m行,opt表示操作标号

  • opt=1 o p t = 1 则为操作 1 1 ,之后有三个数l,r,k 表示查询 k k 在区间[l,r]的排名
  • opt=2 o p t = 2 则为操作 2 2 ,之后有三个数l,r,k 表示查询区间 [l,r] [ l , r ] 内排名为 k k 的数
  • opt=3 则为操作 3 3 ,之后有两个数pos,k 表示将 pos p o s 位置的数修改为 k k
  • opt=4 则为操作 4 4 ,之后有三个数l,r,k 表示查询区间 [l,r] [ l , r ] k k 的前驱
  • opt=5 则为操作 5 5 ,之后有三个数l,r,k 表示查询区间 [l,r] [ l , r ] k k 的后继

输出格式:

对于操作1,2,4,5各输出一行,表示查询结果

输入输出样例

输入样例#1:
9 6
4 2 2 1 9 4 0 1 1
2 1 4 3
3 4 10
2 1 4 3
1 2 5 9
4 3 9 5
5 2 8 5
输出样例#1:
2
4
3
4
9

说明

时空限制: 2s128M 2 s , 128 M
n,m5104 n , m ≤ 5 ⋅ 10 4
保证有序序列所有值在任何时刻满足 [0,108] [ 0 , 10 8 ]

题目来源:bzoj3196 / Tyvj1730 二逼平衡树,在此鸣谢
此数据为洛谷原创。(特别提醒:此数据不保证操作5、6一定存在,故请务必考虑不存在的情况)

解题分析

主席树是个好东西啊, 不用写线段树套平衡树(蒟蒻也不会写QAQ)。很容易看出来这道题就是道带修主席树, 我们就可以用一颗普通主席树保存原树, 树状数组套权值线段树存修改, 查询时两棵树差分一下即可。

不过比较坑的是各种边界判定, 调了博主半天…洛谷AC的代码BZOJ也会被卡掉

总复杂度 O(Nlog2(N)) O ( N l o g 2 ( N ) ) (套平衡树 O(Nlog3(N)) O ( N l o g 3 ( N ) )

代码如下:

#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <limits.h>
#include <cmath>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100050
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define lbt(i) (i & -i)
bool neg;
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) {if(c == '-') neg = true; c = gc;}
    W (isdigit(c))
    x = (x << 1) + (x << 3) + c - 48, c = gc;
    if(neg) neg = false, x = -x;
}
int dot, q, dif, bcnt, arr, same, rk;
int buf[MX << 1], root[MX], val[MX], deal[MX], rt[MX];
struct OP
{
    int typ, l, r, k;
}op[MX];
struct Node
{
    int son[2], sum;
}tree[MX * 400];
namespace BIT
{
    void modify(int &now, const int &lef, const int &rig, const int &tar, const int &del)//修改树状数组上的主席树
    {
        tree[++arr] = tree[now]; now = arr;
        tree[now].sum += del;
        if(lef == rig) return;
        int mid = lef + rig >> 1;
        if(tar <= mid) modify(ls, lef, mid, tar, del);
        else modify(rs, mid + 1, rig, tar, del);
    }
    IN void add(R int now, const int &tar, const int &del)
    {//del表示修改值, 方便删除
        W (now <= dot)
        modify(rt[now], 1, dif, tar, del), now += lbt(now);
    }
    IN int query(R int now)
    {//查询对应主席树上左儿子的信息。
        R int ret = 0;
        W (now) ret += tree[tree[deal[now]].son[0]].sum, now -= lbt(now);
        return ret;
    }
}
namespace PT
{
    IN void reset(const int &lb, const int &rb)//初始化树状数组将会跳的节点
    {
        for (R int i = lb; i; i -= lbt(i)) deal[i] = rt[i];
        for (R int i = rb; i; i -= lbt(i)) deal[i] = rt[i];
    }
    IN int getid(const int &now) {return std::lower_bound(buf + 1, buf + 1 + dif, now) - buf;}
    void build(int &now, const int &lef, const int &rig)//建立空树
    {
        now = ++arr;
        if(lef == rig) return;
        int mid = lef + rig >> 1;
        build(ls, lef, mid); build(rs, mid + 1, rig);
    }
    void insert(const int &pre, int &now, const int &lef, const int &rig, const int &tar)//插入静态主席树
    {
        tree[now = ++arr] = tree[pre];
        ++tree[now].sum;
        if(lef == rig) return;
        int mid = lef + rig >> 1;
        if(tar <= mid) insert(tree[pre].son[0], ls, lef, mid, tar);
        else insert(tree[pre].son[1], rs, mid + 1, rig, tar);
    }
    int getrank(const int &l, const int &r, const int &lb, const int &rb, const int &lef, const int &rig, const int &tar)
    {
        if(lef == rig) return 1;//因为查询到的是比当前值小的总共有多少点, 所以实际上排名要+1
        int mid = lef + rig >> 1;
        if(tar <= mid)
        {
            for (R int i = r; i; i -= lbt(i)) deal[i] = tree[deal[i]].son[0];
            for (R int i = l; i; i -= lbt(i)) deal[i] = tree[deal[i]].son[0];
            return getrank(l, r, tree[lb].son[0], tree[rb].son[0], lef, mid, tar);
        }
        else
        {
            int rem = BIT::query(r) - BIT::query(l) + tree[tree[rb].son[0]].sum - tree[tree[lb].son[0]].sum;
            for (R int i = r; i; i -= lbt(i)) deal[i] = tree[deal[i]].son[1];
            for (R int i = l; i; i -= lbt(i)) deal[i] = tree[deal[i]].son[1];
            return getrank(l, r, tree[lb].son[1], tree[rb].son[1], mid + 1, rig, tar) + rem;//记得加上左子树信息
        }
    }
    int findkth(const int &l, const int &r, const int &lb, const int &rb, const int &lef, const int &rig, const int &kth)
    {
        if(lef == rig) return buf[lef];
        int sum = BIT::query(r) - BIT::query(l) + tree[tree[rb].son[0]].sum - tree[tree[lb].son[0]].sum;//查询当前区间信息
        int mid = lef + rig >> 1;
        if(sum >= kth)
        {
            for (R int i = r; i; i -= lbt(i)) deal[i] = tree[deal[i]].son[0];
            for (R int i = l; i; i -= lbt(i)) deal[i] = tree[deal[i]].son[0];
            return findkth(l, r, tree[lb].son[0], tree[rb].son[0], lef, mid, kth);
        }
        else
        {
            for (R int i = r; i; i -= lbt(i)) deal[i] = tree[deal[i]].son[1];
            for (R int i = l; i; i -= lbt(i)) deal[i] = tree[deal[i]].son[1];
            return findkth(l, r, tree[lb].son[1], tree[rb].son[1], mid + 1, rig, kth - sum);
        }
    }
    int getsuc(const int &l, const int &r, const int &lb, const int &rb, const int &tar)
    {
        reset(l, r);
        int kth = getrank(l, r, lb, rb, 1, dif, tar + 1);
        reset(l, r);
        int ans = findkth(l, r, lb, rb, 1, dif, kth);
        if(ans != tar && kth <= r - l) return ans;//注意这个条件判定
        else return INT_MAX;
    }
    int getpre(const int &l, const int &r, const int &lb, const int &rb, const int &tar)
    {
        reset(l, r);
        int kth = getrank(l, r, lb, rb, 1, dif, tar - 1);
        reset(l, r);
        if(kth <= 1) return -INT_MAX;
        return findkth(l, r, lb, rb, 1, dif, kth);
    }
}
int main(void)
{
    in(dot), in(q);
    for (R int i = 1; i <= dot; ++i) in(val[i]), buf[++bcnt] = val[i];
    for (R int i = 1; i <= q; ++i)
    {
        in(op[i].typ);
        if(op[i].typ == 3) in(op[i].l), in(op[i].r), buf[++bcnt] = op[i].r;
        else {in(op[i].l), in(op[i].r), in(op[i].k); if(op[i].typ != 2) buf[++bcnt] = op[i].k;}//把所有可能查到的点都插入进去
    }
    std::sort(buf + 1, buf + 1 + bcnt);
    dif = std::unique(buf + 1, buf + 1 + bcnt) - buf - 1;
    PT::build(root[0], 1, dif);
    for (R int i = 1; i <= dot; ++i)
    PT::insert(root[i - 1], root[i], 1, dif, PT::getid(val[i]));
    for (R int i = 1; i <= q; ++i)
    {
        switch (op[i].typ)
        {
            case 1:
                PT::reset(op[i].l - 1, op[i].r);
                printf("%d\n", PT::getrank(op[i].l - 1, op[i].r, root[op[i].l - 1], root[op[i].r], 1, dif, PT::getid(op[i].k)));
                break;
            case 2:
                PT::reset(op[i].l - 1, op[i].r);
                printf("%d\n", PT::findkth(op[i].l - 1, op[i].r, root[op[i].l - 1], root[op[i].r], 1, dif, op[i].k));
                break;
            case 3:
                BIT::add(op[i].l, PT::getid(val[op[i].l]), -1);
                BIT::add(op[i].l, PT::getid(op[i].r), 1);
                val[op[i].l] = op[i].r; break;
            case 4:
                printf("%d\n", PT::getpre(op[i].l - 1, op[i].r, root[op[i].l - 1], root[op[i].r], PT::getid(op[i].k))); break;
            case 5:
                printf("%d\n", PT::getsuc(op[i].l - 1, op[i].r, root[op[i].l - 1], root[op[i].r], PT::getid(op[i].k))); break;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值