权值线段树

权值线段树的功能有

  • 查询x在整个区间出现的次数
  • 查询[L,R]的数字出现的次数
  • 所有数中出现次数第k大的数字

基于线段树和二分的思想

定义(int tree[maxn];)
tree[i]表示某段区间数字出现的次数

一般需要离散化操作

插入数据

void update(int l,int r,int rt,int x,int op){//插入op个x
    if(l == r){
        tree[rt] += op;
        return;
    }
    int mid = (l + r) >> 1;
    if(x <= mid)update(lson,x,op);
    else update(rson,x,op);
    pushup(rt);
}

查询x在整个区间出现的次数

int find(int l,int r,int rt,int x){//查询x在整个区间出现的次数
    if(l == r)return tree[rt];
    int mid = (l + r) >> 1;
    if(x <= mid)return find(lson,x);
    else return find(rson,x);
}

查询[L,R]的数字出现的次数

int find2(int l,int r,int rt,int L,int R){//查询[L,R]的数字出现的次数
    if(L <= l && r <= R)return tree[rt];
    int mid = (l + r) >> 1;
    if(R <= mid)return find2(lson,L,R);
    if(L > mid)return find2(rson,L,R);
    return find2(lson,L,R) + find2(rson,L,R);
}

查询第k大数

只需要知道右节点数字出现的次数即可
权值线段树是查询整个区间的,主席树是对于区间查询第k值的

int kth(int l,int r,int rt,int k){//所有数中出现次数第k大的数字
    if(l == r)return l;
    int mid = (l + r) >> 1;
    int rs = tree[rs(rt)];//只看右结点即可
    if(k <= rs)return kth(rson,k);//在右边
    else return kth(lson,k - rs);//在左边,变为第k - rs大的数字
}

模板

#include <iostream>
#include <cstdio>
#define ls(rt) rt<<1
#define rs(rt) rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const int maxn = 1e5 + 5;
int tree[maxn >> 2];//下标是数字,tree[i]某段区间数字出现的次数
void pushup(int rt){
    tree[rt] = tree[ls(rt)] + tree[rs(rt)];
}
void update(int l,int r,int rt,int x,int op){//插入op个x
    if(l == r){
        tree[rt] += op;
        return;
    }
    int mid = (l + r) >> 1;
    if(x <= mid)update(lson,x,op);
    else update(rson,x,op);
    pushup(rt);
}
int find(int l,int r,int rt,int x){//查询x在整个区间出现的次数
    if(l == r)return tree[rt];
    int mid = (l + r) >> 1;
    if(x <= mid)return find(lson,x);
    else return find(rson,x);
}
int find2(int l,int r,int rt,int L,int R){//查询[L,R]的数字出现的次数
    if(L <= l && r <= R)return tree[rt];
    int mid = (l + r) >> 1;
    if(R <= mid)return find2(lson,L,R);
    if(L > mid)return find2(rson,L,R);
    return find2(lson,L,R) + find2(rson,L,R);
}
int kth(int l,int r,int rt,int k){//所有数中出现次数第k大的数字
    if(l == r)return l;
    int mid = (l + r) >> 1;
    int rs = tree[rs(rt)];//只看右结点即可
    if(k <= rs)return kth(rson,k);//在右边
    else return kth(lson,k - rs);//在左边,变为第k - rs大的数字
}
// int rank(int l,int r,int rt,int x){//查询x在全局的排名

// }
// int pre(int l,int r,int rt,int x){//查询前驱,小于等于x的最大

// }
// int ore(int l,int r,int rt,int x){//查询后继,大于x的最大值

// }
int n;
int main(){
    int m;
    cin >> n >> m;
    int x;
    for(int i = 0; i < n; i++){
        scanf("%d",&x);
        update(1,n,1,x,1);
    }
    
    return 0;
}
原文地址:https://www.cnblogs.com/Emcikem/p/12175702.html