P3380 【模板】二逼平衡树(树套树)
作者:互联网
- 查询 k k k 在区间内的排名
- 查询区间内排名为 k k k的值
- 修改某一位值上的数值
- 查询 k k k 在区间内的前驱(前驱定义为严格小于 x x x,且最大的数,若不存在输出 − 2147483647 -2147483647 −2147483647)
- 查询 k k k 在区间内的后继(后继定义为严格大于 x x x,且最小的数,若不存在输出 2147483647 2147483647 2147483647)
分析
对于静态区间第K大来说,那就上主席树吧
如果动态的话,就树套树了
如果是双动态的话,那就树套树套树了(大雾)
此时我们只需要实现三个功能即可
- 根据值查询排名
- 根据排名查值
- 修改值
对于一棵静态区间第K大的主席树来说,维护的是一个前缀和
一旦某一个版本(位置)的值改变了,那么必须重构这个版本(位置)往后的所有版本状态
此时修改复杂度最差为
n
∗
l
o
g
(
n
)
n*log(n)
n∗log(n),我不能接受
那么什么能够在短时间维护一个前缀和的修改呢?
我们想到了树状数组,每次
l
o
g
(
n
)
log(n)
log(n)时间修改一个位置
查询也是
l
o
g
(
n
)
log(n)
log(n)的
我们对树状数组和主席树进行组装,树状数组的每一个节点都表示着主席树的某一系列版本
对于要查询某个区间的版本情况,找到需要的左端点版本对应的一系列树状数组维护的版本
l
−
1
l-1
l−1,以及右端点版本
r
r
r(共有将近
l
o
g
(
n
)
log(n)
log(n)个版本,使用 lowbit 预处理出这一系列版本)
在这两个主席树系列版本上进行差分
修改值也是先预处理出这一系列版本,在这 l o g ( n ) log(n) log(n)个版本中更新
对于第三第四个操作
通过第一个和第二个操作的组合,就能实现
代码
//P3380
/*
@Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("P3380_1.in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;
int arr[MAX_N];
int uniarr[MAX_N];
int unicnt = 0;
struct Qr {
int opt, l, r, k;
}qr[MAX_N];
struct Tr {
int k, l, r;
}tr[MAX_N*25];
int indx = 0;
int root[MAX_N];
void update(int&rt, int l, int r, int x, int k) {
if (!rt) rt = ++indx;
tr[rt].k += k;
if (l == r) {
return;
}
int mid = l + ((r-l)>>1);
if (x <= mid) update(tr[rt].l, l, mid, x, k);
else update(tr[rt].r, mid+1, r, x, k);
}
int query(int rt, int l, int r, int x, int y) {
if (!rt) return 0;
if (x <= l && r <= y) {
return tr[rt].k;
}
int mid = l + ((r-l)>>1);
if (y <= mid) return query(tr[rt].l, l, mid, x, y);
if (x > mid) return query(tr[rt].r, mid+1, r, x, y);
return query(tr[rt].l, l, mid, x, y) + query(tr[rt].r, mid+1, r, x, y);
}
int cnt[2];
int son[2][MAX_N];
int kth(int l, int r, int k) {
if (l == r) {
return l;
}
int mid = l + ((r-l)>>1);
int lsum = 0, sum = 0;
for (int i = 1; i <= cnt[1]; ++i) {
sum += tr[son[1][i]].k;
lsum += tr[tr[son[1][i]].l].k;
}
for (int i = 1; i <= cnt[0]; ++i) {
sum -= tr[son[0][i]].k;
lsum -= tr[tr[son[0][i]].l].k;
}
if (lsum >= k) {
for (int i = 1; i <= cnt[0]; ++i) {
son[0][i] = tr[son[0][i]].l;
}
for (int i = 1; i <= cnt[1]; ++i) {
son[1][i] = tr[son[1][i]].l;
}
return kth(l, mid, k);
} else {
for (int i = 1; i <= cnt[0]; ++i) {
son[0][i] = tr[son[0][i]].r;
}
for (int i = 1; i <= cnt[1]; ++i) {
son[1][i] = tr[son[1][i]].r;
}
return kth(mid+1, r, k - lsum);
}
}
int rak(int x) {
int sum = 0;
int t;
for (int i = 1; i <= cnt[1]; ++i) {
t = query(son[1][i], 1, unicnt, 1, x);
sum += t;
}
for (int i = 1; i <= cnt[0]; ++i) {
t = query(son[0][i], 1, unicnt, 1, x);
sum -= t;
}
return sum;
}
inline int lowbit(int x) {
return x&-x;
}
void modify(int x, int k, int w) {
while (x <= N) {
update(root[x], 1, unicnt, k, w);
x += lowbit(x);
}
}
void init(int l, int r) {
memset(cnt, 0, sizeof cnt);
--l;
while (l) {
son[0][++cnt[0]] = root[l];
l -= lowbit(l);
}
while (r) {
son[1][++cnt[1]] = root[r];
r -= lowbit(r);
}
}
/**
9 1
4 2 2 1 9 4 0 1 1
2 1 4 3
**/
void solve(){
sc("%lld%lld", &N, &M);
uniarr[++unicnt] = -2147483647;
uniarr[++unicnt] = 2147483647;
for (int i = 1; i <= N; ++i) {
sc("%lld", &arr[i]);
uniarr[++unicnt] = arr[i];
}
int opt, l, r, k;
for (int i = 1; i <= M; ++i) {
sc("%lld%lld%lld", &qr[i].opt, &qr[i].l, &qr[i].r);
if (qr[i].opt == 1) {
sc("%lld", &qr[i].k);
uniarr[++unicnt] = qr[i].k;
} else if (qr[i].opt == 2) {
sc("%lld", &qr[i].k);
} else if (qr[i].opt == 3) {
qr[i].k = qr[i].r;
uniarr[++unicnt] = qr[i].k;
} else if (qr[i].opt == 4) {
sc("%lld", &qr[i].k);
uniarr[++unicnt] = qr[i].k;
} else {
sc("%lld", &qr[i].k);
uniarr[++unicnt] = qr[i].k;
}
}
sort(uniarr+1, uniarr+1+unicnt);
unicnt = unique(uniarr+1, uniarr+1+unicnt) - uniarr - 1;
for (int i = 1; i <= N; ++i) {
arr[i] = lower_bound(uniarr+1, uniarr+1+unicnt, arr[i]) - uniarr;
modify(i, arr[i], 1);
}
for (int i = 1; i <= M; ++i) {
if (qr[i].opt != 2) qr[i].k = lower_bound(uniarr+1, uniarr+1+unicnt, qr[i].k) - uniarr;
if (qr[i].opt == 1) {
init(qr[i].l, qr[i].r);
pr("%lld\n", rak(qr[i].k-1) + 1);
} else if (qr[i].opt == 2) {
init(qr[i].l, qr[i].r);
pr("%lld\n", uniarr[kth(1, unicnt, qr[i].k)]);
} else if (qr[i].opt == 3) {
modify(qr[i].l, arr[qr[i].l], -1);
modify(qr[i].l, arr[qr[i].l] = qr[i].k, 1);
} else if (qr[i].opt == 4) {
init(qr[i].l, qr[i].r);
int rk = rak(qr[i].k-1);
init(qr[i].l, qr[i].r);
pr("%lld\n", uniarr[kth(1, unicnt, rk)]);
} else {
init(qr[i].l, qr[i].r);
int rk = rak(qr[i].k);
init(qr[i].l, qr[i].r);
pr("%lld\n", uniarr[kth(1, unicnt, rk+1)]);
}
}
}
signed main()
{
#ifndef ONLINE_JUDGE
FILE_IN
FILE_OUT
#endif
int T = 1;//cin >> T;
while (T--) solve();
return AC;
}
标签:qr,树套,int,uniarr,tr,son,unicnt,P3380,模板 来源: https://blog.csdn.net/qq_45754027/article/details/120293108