【动态开点线段树】洛谷P3369 【模板】普通平衡树
作者:互联网
本题使用动态开点权值线段树...
数据返回是 [-1e7,1e7] ,所以将其 +(1e7+1) 让范围映射到 [1,2e7+1] ,这样就在 [1,2e7+1] 构建权值线段树,记 2e7+1 为 MAXN;
先考虑前面 4 种操作
插入和删除操作就是在线段树的叶子节点(递归的最底层)的位置 +-1,表示该点的个数,向上 push_up 维护区间 [L,R] 拥有的数字个数
根据所给数字,这里记作tar,求排名:就是求区间 [1,tar-1] 有多少个数字,求出来要记得 +1,因为是 tar 的排名
根据排名求数字,可以通过二分数字,对于每一个数字求排名,这样就可以用到步骤三写的函数
找前驱后继,这里记 tar 为所要找的数字:
除了以上维护的区间数字个数 cnt,再维护两个信息区间最大值 maxn 和区间最小值 minn
找前驱就是 [1,tar-1] 找一个最大值,找后继就是 [tar+1,MAXN] 找一个最小值
#include <bits/stdc++.h>
using namespace std;
#define MS 100009
#define ls rt<<1
#define rs rt<<1|1
#define LL long long
#define MAXN 20000009
int n,m;
int tot;
int root;
struct node{
int l,r;
int cnt;
int maxn,minn;
}p[MS*20];
void push_up(int &rt){
if(!p[rt].l && !p[rt].r){ // 无左右子节点,删除本节点
rt = 0;
}
else if(!p[rt].l){ // 无左节点
p[rt].maxn = p[p[rt].r].maxn;
p[rt].minn = p[p[rt].r].minn;
p[rt].cnt = p[p[rt].r].cnt;
}
else if(!p[rt].r){ // 无右节点
p[rt].maxn = p[p[rt].l].maxn;
p[rt].minn = p[p[rt].l].minn;
p[rt].cnt = p[p[rt].l].cnt;
}
else{ // 左右节点都存在
p[rt].maxn = max(p[p[rt].l].maxn,p[p[rt].r].maxn);
p[rt].minn = min(p[p[rt].l].minn,p[p[rt].r].minn);
p[rt].cnt = p[p[rt].l].cnt + p[p[rt].r].cnt;
}
}
void update(int &rt,int l,int r,int pos,int val){
if(!rt) rt = ++tot; // 动态开点
if(l == r && l == pos){
p[rt].cnt += val; // val对应 +-1 表示插入或删除
if(p[rt].cnt > 0) // 本节点存在值
p[rt].maxn = p[rt].minn = l;
else rt = 0; // 删除
return;
}
int m = l+r>>1;
if(m >= pos) update(p[rt].l,l,m,pos,val);
else update(p[rt].r,m+1,r,pos,val);
push_up(rt);
}
int getRank(int L,int R,int l,int r,int rt){ // 其实就是求 [L,R] 存在的节点总和
if(!rt) return 0; // 不存在该节点
if(L <= l && r <= R){
return p[rt].cnt;
}
int ans = 0;
int m = l+r>>1;
if(m >= L) ans += getRank(L,R,l,m,p[rt].l);
if(m < R) ans += getRank(L,R,m+1,r,p[rt].r);
return ans;
}
int getFront(int L,int R,int l,int r,int rt){ // 就是求 [L,R] 最大值
if(!rt) return 0; // 不存在该节点
if(L <= l && r <= R){
return p[rt].maxn;
}
int ans = 0;
int m = l+r>>1;
if(m >= L) ans = max(ans,getFront(L,R,l,m,p[rt].l));
if(m < R) ans = max(ans,getFront(L,R,m+1,r,p[rt].r));
return ans;
}
int getLater(int L,int R,int l,int r,int rt){ // 就是求 [L,R] 最小值
if(!rt) return MAXN; // 不存在该节点
if(L <= l && r <= R){
return p[rt].minn;
}
int ans = MAXN;
int m = l+r>>1;
if(m >= L) ans = min(ans,getLater(L,R,l,m,p[rt].l));
if(m < R) ans = min(ans,getLater(L,R,m+1,r,p[rt].r));
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin >> n;
while(n--){
int op,tar,rank;
cin >> op >> tar;
rank = tar;
tar += 10000001; // [1,2e7+1]
if(op == 1){
update(root,1,MAXN,tar,1);
}
else if(op == 2){
update(root,1,MAXN,tar,-1);
}
else if(op == 3){
if(tar == 1) cout << 1 << "\n";
else cout << getRank(1,tar-1,1,MAXN,root)+1 << "\n";
}
else if(op == 4){
int l=1,r=20000001;
int ans;
while(l <= r){
int mid = l+r>>1;
int num = getRank(1,mid,1,MAXN,root);
if(num >= rank) ans = mid ,r = mid-1;
else l = mid+1;
}
cout << ans-10000001 << "\n";
}
else if(op == 5){
int ans = getFront(1,tar-1,1,MAXN,root);
cout << ans-10000001 << "\n";
}
else if(op == 6){
int ans = getLater(tar+1,MAXN,1,MAXN,root);
cout << ans-10000001 << "\n";
}
}
return 0;
}
// 关于各函数调用:
// rt位置,一定要填 root,而不是单纯的 1;
// 因为当一个数插入又删除导致整棵树为空后,再次插入时树根节点不再是 1
// 相当于之前的空树都不要了;
// root 始终指向线段树的根节点;
// 每一次树空了 root就会变化;
/*
input:
20
1 9508226
1 -9775935
2 9508226
2 -9775935
1 -6414629
3 -6414629
2 -6414629
1 -1847925
5 3248500
2 -1847925
1 -2757289
1 -7942063
1 -5545399
2 -7942063
1 6169408
5 -648755
1 -275474
1 -9836404
1 -3389358
1 -8467606
output:
1
-1847925
-2757289
*/
标签:rt,洛谷,tar,int,P3369,ans,return,MAXN,模板 来源: https://www.cnblogs.com/Tecode/p/14644995.html