洛谷传送门(数据水)
BZOJ传送门
题目描述(洛谷)
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
- 查询k在区间内的排名
- 查询区间内排名为k的值
- 修改某一位值上的数值
- 查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)
- 查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647)
注意上面两条要求和tyvj或者bzoj不一样,请注意(说明数据是rand的…)
输入输出格式
输入格式:
第一行两个数 n,m n , m 表示长度为 n n 的有序序列和个操作
第二行有 n n 个数,表示有序序列
下面有行,opt表示操作标号
- 若 opt=1 o p t = 1 则为操作 1 1 ,之后有三个数 表示查询 k k 在区间的排名
- 若 opt=2 o p t = 2 则为操作 2 2 ,之后有三个数 表示查询区间 [l,r] [ l , r ] 内排名为 k k 的数
- 若 则为操作 3 3 ,之后有两个数 表示将 pos p o s 位置的数修改为 k k
- 若 则为操作 4 4 ,之后有三个数 表示查询区间 [l,r] [ l , r ] 内 k k 的前驱
- 若 则为操作 5 5 ,之后有三个数 表示查询区间 [l,r] [ l , r ] 内 k k 的后继
输出格式:
对于操作各输出一行,表示查询结果
输入输出样例
输入样例#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
说明
时空限制:
2s,128M
2
s
,
128
M
n,m≤5⋅104
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;
}
}
}