主席树模板(一)

引入

首先请求出:

​ 长度为n的序列

​ m次询问全局第k小

做法:

​ 画一棵(权值)线段树手动模拟,请记住此过程

之后,请思考;

​ 长度为n的序列

​ m次询问区间[l, r]中第k小值

​ 值域 ±1e9

​ n≤2e5 , m≤2e5

做法: 可持久化线段树

原理

用 [1, r]建得的线段树 - [1, l-1]建得的线段树 (即把这两颗形状一样的线段树上的每个节点的权值相减), 得到的即为区间[l, r]建得的线段树, 这样之后,我们就可以求出(这棵线段树)全局第k小值,而这个值也就是区间[l, r]的第k小值

代码实现

#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 200000+99

int n, m;

struct da
{
    int n,a,k;//输入顺序n,原数据a,离散化之后数据k 
}a[MAX];
int ys[MAX];//da数组和原数的映射关系 

bool cmp1(da x, da xxx) {
    return x.a < xxx.a ;//用于离散化 
}
bool cmp2(da x, da xxx) {
    return x.n < xxx.n ;
}

int root[MAX];//保存森林中的各个根节点 //时间戳? 
int nodecnt;//所有节点的数量 
struct tree{
    int sum;
    int lson, rson;
}tr[MAX<<5];//2n+nlogn

int build(int l, int r) {//建空树 
    int now = ++nodecnt;//加点 
    if(l == r) return now;
    int mid = (l+r)>>1;
    tr[now].lson = build(l, mid);
    tr[now].rson = build(mid+1, r);
    return now;
}

int insert(int last, int l, int r, int x) {//建权值线段树 
    int now = ++nodecnt;
    tr[now].sum = tr[last].sum + 1;
    tr[now].lson = tr[last].lson , tr[now].rson = tr[last].rson;//先继承上一棵树再说
    if(l == r) return now;
    int mid =  (l+r)>>1;
    if(x <= mid) tr[now].lson = insert(tr[last].lson , l, mid, x);
    else tr[now].rson = insert(tr[last].rson , mid+1, r, x);
    return now;
}


int query(int ltree, int rtree, int l, int r, int k) {//同时跳 
    if(l == r) return l;
    int mid = (l+r)>>1;
    int tmp = tr[tr[rtree].lson ].sum - tr[tr[ltree].lson ].sum ;
    if(tmp >= k) return query(tr[ltree].lson , tr[rtree].lson , l, mid, k);
    else return query(tr[ltree].rson , tr[rtree].rson , mid+1, r, k-tmp);
}


int main() {
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++) scanf("%d",&a[i].a ), a[i].n = i;
    sort(a+1, a+1+n, cmp1);
    int tot = 0;
    for(int i = 1; i <= n; i++) {
        a[i].k = ++tot;
        ys[tot] = a[i].a ;
        while(a[i+1].a == a[i].a) a[++i].k = tot;
    }
    sort(a+1, a+1+n, cmp2);
//  for(int i = 1; i <= n; i++) printf("\n lsh : %d\n",a[i].k );
    root[0] = build(1,n);
    for(int i = 1;i <= n; i++)//边加点边建树 
        root[i] = insert(root[i-1], 1, n, a[i].k) ; 
    int ans, l, r, k; 
    for(int i = 1; i <= m; i++) {
        scanf("%d%d%d", &l, &r, &k);
        ans = query(root[l-1], root[r], 1, n, k);
        printf("%d\n", ys[ans]);
    }
}

猜你喜欢

转载自www.cnblogs.com/tyner/p/11253785.html