AcWing 255. 第K小数 (主席树写法)

区间k小数是主席树的模板题目,如果区间不包含,用莫队+权值线段树也能解

主席树是可持久化线段树,所为可持久化,就是每次只新增不一样的节点,而保留前面的版本,这样可以做到查询。

如果询问时1-r,那么直接主席树,询问的是l-r,就用到前缀和思想,具体看代码注释

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
const int N=3e5+10;
vector<int> num;
int a[N];
int root[N];
int idx;
struct node{  //左儿子,右儿子,个数
    int l,r;
    int cnt;
}tr[N*4+N*17];  //注意大小
int find(int x){  //离散化后二分查找,+1是因为我习惯从1-n建树,不+1就是0-n-1建树,是一样的
    return lower_bound(num.begin(),num.end(),x)-num.begin()+1;
}
int build(int l,int r){ //初始建树操作,其实没有必要
    int q=++idx;
    if(l==r){
        return q;
    }
    int mid=l+r>>1;
    tr[q].l=build(l,mid);
    tr[q].r=build(mid+1,r);
    return q;
}
int insert(int p,int l,int r,int x){
    int q=++idx;//为新节点开辟空间
    tr[q]=tr[p];//持久化的原理,先复制前面一版的信息
    if(l==r){
        tr[q].cnt++;
        return q;
    }
    int mid=l+r>>1;
    if(x<=mid)
    tr[q].l=insert(tr[p].l,l,mid,x);//如果在左儿子改变,与前一版本的区别就是左儿子
    else
    tr[q].r=insert(tr[p].r,mid+1,r,x);
    tr[q].cnt=tr[tr[q].r].cnt+tr[tr[q].l].cnt; //记得更新区间的个数
    return q;
}
int query(int p,int q,int l,int r,int k){
    if(l==r){
        return l;
    }
    int cnt=tr[tr[q].l].cnt-tr[tr[p].l].cnt;  //前缀和思想,l-r其实就是r版本-l-1版本的个数
    int mid=l+r>>1;
    if(k<=cnt)
    return query(tr[p].l,tr[q].l,l,mid,k);
    else
    return query(tr[p].r,tr[q].r,mid+1,r,k-cnt); //k-cnt是减去左节点的个数
}
int main(){
    int i;
    int n;
    int m;
    cin>>n>>m;
    for(i=1;i<=n;i++){
        cin>>a[i];
        num.push_back(a[i]);
    }
    sort(num.begin(),num.end());
    num.erase(unique(num.begin(),num.end()),num.end()); //离散化
    root[0]=build(1,num.size());
    for(i=1;i<=n;i++){
        root[i]=insert(root[i-1],1,num.size(),find(a[i]));
    }
    while(m--){
        int l,r,k;
        cin>>l>>r>>k;
        cout<<num[query(root[l-1],root[r],1,num.size(),k)-1]<<endl;
    }
} 

原文地址:https://www.cnblogs.com/ctyakwf/p/12256624.html

时间: 2024-07-29 14:33:15

AcWing 255. 第K小数 (主席树写法)的相关文章

POJ 2104 求序列里第K大 主席树裸体题

给定一个n的序列,有m个询问 每次询问求l-r 里面第k大的数字是什么 只有询问,没有修改 可以用归并树和划分树(我都没学过..囧) 我是专门冲着弄主席树来的 对主席树的建树方式有点了解了,不过这题为什么是在主席树里面这么操作的 还是有点不懂,今天照着模板敲了一遍就打多校了 再研究吧 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using name

bzoj2588: Spoj 10628. Count on a tree(树上第k大)(主席树)

每个节点继承父节点的树,则答案为query(root[x]+root[y]-root[lca(x,y)]-root[fa[lca(x,y)]]) #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<algorithm> using namespace std; const int maxn=100010; struct poi{int s

poj 2104 (主席树写法)

//求第K的的值 1 #include<stdio.h> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 typedef long long LL; 7 const int maxn = 1e5 + 10; 8 int n, m, cnt; 9 struct node { 10 int L, R, sum; 11 } tree[ma

[主席树]HDOJ4417 Super Mario

题意:n个数 m个询问  ($n.m \le 10^5$) 每个询问有l, r, k  问的是[l, r]区间内有多少个数小于等于k 用主席树做的话查询第i小的数与k比较即可 1 #define lson l, m 2 #define rson m+1, r 3 const int N=1e5+5; 4 int L[N<<5], R[N<<5], sum[N<<5]; 5 int tot; 6 int a[N], T[N], Hash[N]; 7 int build(i

【SDUT OJ 2610】 Boring Counting(主席树)

[SDUT OJ 2610] Boring Counting(主席树) Boring Counting Time Limit: 3000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 In this problem you are given a number sequence P consisting of N integer and Pi is the ith element in the sequence. Now you task is to ans

HDU-6704 K-th occurrence(后缀数组+主席树)

题意 给一个长度为n的字符串,Q次询问,每次询问\((l,r,k)\) , 回答子串\(s_ls_{l+1}\cdots s_r\) 第\(k\) 次出现的位置,若不存在输出-1.\(n\le 1e5,Q\le 1e5\) 分析 查询子串第 k 次出现的位置,很容易想到要用处理字符串的有力工具--后缀数组. 那么该怎么用呢?我们先把样例的字符串的每个后缀排个序,然后对样例进行模拟 原串:aaabaabaaaab 排名 后缀 位置 1 aaaab 8 2 aaab 9 3 aaabaabaaab

[知识学习] 主席树

这两天学习了主席树,基本上搞懂了主席树是怎么操作的 主席树,是一种可持久化线段树.最简单的操作就是维护静态区间第 \(k\) 小 主席树通过维护历史版本,实现查询区间的有关操作 主席树的原理 假设现在有这么一个序列:\(4,1,3,5,2\) 问如何求出区间[1,3]内大小为第二的数? 利用大眼观察法,很显然是3 那么让计算机去怎么实现呢?它又没有眼睛 对于这个序列,我们可以先建一颗空的权值线段树,命名为"树0"(方便后面的使用),如图: 别告诉我你不知道什么是权值线段树,自己去百度:

【Luogu】P3384主席树模板(主席树查询K小数)

YEAH!我也是一个AC主席树模板的人了! 其实是个半吊子 我将尽量详细的讲出我的想法. 主席树太难,我们先搞普通线段树好了 普通线段树怎么做?我的想法是查询K次最小值,每次查完把查的数改成INF,查完再改回来... MDZZ 于是就有了主席树. 先不考虑主席树,我们来考虑一个奇特的线段树. 一般的线段树,数列位置是下标,而把数列维护值作为线段树中存的元素. 那我们如果反过来,把数列元素当做线段树的下标...??? 比如说数列[4 2 3 1] 如果线段树的下标是1.2.3.4......? 那

[知识点]主席树入门 区间k值

入坑主席树主要是因为昨天考试的后两道题不可改2333 而且觉得这个还挺有用,于是果断入坑 不过蒟蒻的我只是在上午看了看大概思路,下午开的运动会没时间码,于是晚上码了出来.但是目前只会无修改的区间K值问题,加上又比较抽象,思路屡了半天才刚刚醒悟,于是写下来记录一下. 不扯那么多辣鸡套路,直接说思路打法(以k小值为例): 我们先要以权值建一颗线段树,然后每个节点记录的是这个节点管辖的范围内数列中数的个数. 我们需要建好多好多线段树.简单来讲呢,就是[1,1],[1,2]···[1,n]这么多颗,每棵