静态区间第K小
给出一段长度序列 a[1]、a[2]、… 、a[N],每次询问 [L, R] 区间内第K小的数为?
我们可以建立 权值线段树,线段树内存储 某个权值的出现次数。
对于 [L, R] 区间(a[L] ~ a[R]),构建的线段树
以下示例参考blog:https://www.cnblogs.com/LiuRunky/p/Sustainable_Segment_Tree.html
例如:数列a={10,20,30,20,50,10,60,40},离散化后得到b={1,2,3,2,5,1,6,4}
对于数列内每一个离散化后的数,我们建立一个基于数值的 区间和线段树 统计它的出现次数
(7、8是用来占位的,可以无视)
这样,我们可以通过类似二分的思想找到第k小,而线段树的节点已经帮助我们将区间对半切分
假设我们想找区间第7小:
step 1: 区间[1,4]内的数一共出现了6次,所以我们可以直接进入另一区间[5,8],并且找这个区间中的第1小
step 2: 区间[5,6]内的数一共出现了2次,所以[5,8]中的第1小一定也是[5,6]中的第1小
step 3: 区间[5,5]内的数一共出现了1次,所以5正是[5,6]中的第1小,即整个查询区间中的第7小
那要怎么得到区间[L, R]的线段树呢,这里就要用到主席树,先建个出现次数全为0的空树,然后依次更新加入 a[1]、a[2]、… 、a[N],那我们就得到了N个版本的权值线段树(可以看作一个 树形的前缀和)
对于任意一个结点,只需要用 第R个版本 和 第L-1个版本 的线段树同位置结点相减,从L-1 到R这个结点增加的值,既是 [L, R]的线段树的值。
模板题:POJ2104 - K-th Number
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e5+50;
int n,m,N,a[maxn],b[maxn],root[maxn];
int t[maxn<<5],ls[maxn<<5],rs[maxn<<5],cnt=0</