题面
树状数组思路
这题其实是平衡树
的板子…;
用树状数组做需要离线离散化处理;
首先增加删除一个数很容易,直接用树状数组的加一减一即可;
如果要查询数值 x x x的排名
其实相当于问我们1到x-1有多少个数;
那么query
后加一即可;
如果要查第 k k k小的数,这个和往常的树状数组不同,要写一个新的函数;
有两种理解方式,一种是类似倍增LCA
的想法;
另一种是反向求和
;
比如说我们要 13 13 13
我们用二进制的思想去拼凑他, 13 = 8 + 4 + 1 13=8+4+1 13=8+4+1
核心思想就是找到一个小于它但是最大的二的次方数,然后不断的逼近他;
其实就是求一个前缀和,因为前缀和就是排名;
比如求
13
13
13就像下图一样
(其实这两都差不多)
接着要查询小于 x x x的最大的数以及查询大于 x x x的最小的数;
比如说数列为 1 , 2 , 2 , 3 , 4 , 4 1,2,2,3,4,4 1,2,2,3,4,4
我们要查小于
3
3
3的最大数,那么只需要用kth
将
q
u
e
r
y
(
3
−
1
)
query(3-1)
query(3−1)代入即可,也就是算出它多少名;
而大于
3
3
3的最小的数,我们同样用kth
,代入的参数有点不同;
因为 4 4 4有多个,那么我们只需要求出包括 3 3 3在内有多少个数,再将其加一,便得到了排名;
Code
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int tr[N],n;
struct Query{
int opt,x;
}q[N];
vector<int> ve;
int _get(int x){
return lower_bound(ve.begin(),ve.end(),x) - ve.begin() + 1;
}
int lowbit(int x){
return x & -x;
}
void add(int u,int v){
for(int i=u;i<=ve.size();i+=lowbit(i)){
tr[i] += v;
}
}
int query(int x){
int ret = 0;
for(int i=x;i;i-=lowbit(i)){
ret += tr[i];
}
return ret;
}
//返回排名为rank的值
int kth(int rank){
int idx = 0;
//反向求和的过程
for(int i=20;i>=0;--i){
idx += (1<<i);
if(idx > ve.size() || tr[idx]>= rank){
//类似倍增LCA,等于rank不一定是
idx -= (1<<i);
}else{
rank -= tr[idx];
}
}
//+1-1是因为vector从0开始
//如果是从1开始的数组应该为idx+1
return ve[idx+1-1];
}
int main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin >> n;
for(int i=1;i<=n;++i){
cin >> q[i].opt >> q[i].x;
if(q[i].opt != 4) ve.push_back(q[i].x);
}
sort(ve.begin(),ve.end());
ve.erase(unique(ve.begin(),ve.end()),ve.end());
for(int i=1;i<=n;++i){
int op = q[i].opt,x = _get(q[i].x);
if(op == 1){
add(x,1);
}else if(op == 2){
add(x,-1);
}else if(op == 3){
cout << query(x-1) + 1 << '\n';
}else if(op == 4){
cout << kth(q[i].x) << '\n';
}else if(op == 5){
cout << kth(query(x-1)) << '\n';
}else{
cout << kth(query(x) + 1) << '\n';
}
}
return 0;
}
线段树思路
其实就是权值线段树的想法,每个点不再代表区间,而是代表数值域上的点;
要注意下面这份代码与上面的树状数组不同;
数值val的排名上面返回的是最右边,我们这返回的是最左边
因此可以看到调用的时候参数传递的不同;
Code
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n;
struct Query{
int opt,x;
}q[N];
struct Node{
int l,r,sum;
}tr[N<<2];
vector<int> ve;
#define lc (p<<1)
#define rc (p<<1|1)
void build(int p,int l,int r){
tr[p] = {l,r,0};
if(l == r){
return;
}
int mid = (l+r) >> 1;
build(lc,l,mid);
build(rc,mid+1,r);
}
void push_up(int p){
tr[p].sum = tr[lc].sum + tr[rc].sum;
}
//单点加减
void node_add(int p,int k,int val){
if(tr[p].l == tr[p].r){
tr[p].sum += val;
return;
}
int mid = (tr[p].l+tr[p].r) >> 1;
if(k<=mid) node_add(lc,k,val);
else node_add(rc,k,val);
push_up(p);
}
//数值val的排名
int val_kth(int p,int val){
if(tr[p].l == tr[p].r) return 1;
int mid = (tr[p].l+tr[p].r) >> 1;
if(val<=mid) return val_kth(lc,val);
return tr[lc].sum + val_kth(rc,val);
}
//排名为k的数值
int kth(int p,int k){
if(tr[p].l == tr[p].r) return tr[p].l;
if(tr[lc].sum>=k) return kth(lc,k);
return kth(rc,k-tr[lc].sum);
}
int _get(int x){
return lower_bound(ve.begin(),ve.end(),x) - ve.begin() + 1;
}
int main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin >> n;
for(int i=1;i<=n;++i){
cin >> q[i].opt >> q[i].x;
if(q[i].opt != 4) ve.push_back(q[i].x);
}
sort(ve.begin(),ve.end());
ve.erase(unique(ve.begin(),ve.end()),ve.end());
build(1,1,ve.size());
for(int i=1;i<=n;++i){
int op = q[i].opt,x = _get(q[i].x);
if(op == 1){
node_add(1,x,1);
}else if(op == 2){
node_add(1,x,-1);
}else if(op == 3){
cout << val_kth(1,x) << '\n';
}else if(op == 4){
cout << ve[-1+kth(1,q[i].x)] << '\n';
}else if(op == 5){
cout << ve[-1+kth(1,val_kth(1,x)-1)] << '\n';
}else{
cout << ve[-1+kth(1,val_kth(1,x+1))] << '\n';
}
}
return 0;
}