这是个非常经典的可持久化权值线段树入门题——静态区间第 kkk 小。
数据已经过加强,请使用可持久化权值线段树。同时请注意常数优化。
如题,给定 nnn 个整数构成的序列 aaa,将对于指定的闭区间 [l,r][l, r][l,r] 查询其区间内的第 kkk 小值。
第一行包含两个整数,分别表示序列的长度 nnn 和查询的个数 mmm。
第二行包含 nnn 个整数,第 iii 个整数表示序列的第 iii 个元素 aia_iai。
接下来 mmm 行每行包含三个整数 $ l, r, k$ , 表示查询区间 [l,r][l, r][l,r] 内的第 kkk 小值。
对于每次询问,输出一行一个整数表示答案。
5 5
25957 6405 15770 26287 26465
2 2 1
3 4 1
4 5 1
1 2 2
4 4 1
6405
15770
26287
25957
26287
n=5n=5n=5,数列长度为 555,数列从第一项开始依次为{25957,6405,15770,26287,26465}\{25957, 6405, 15770, 26287, 26465\}{25957,6405,15770,26287,26465}。
对于 20%20\%20% 的数据,满足 1≤n,m≤101 \leq n,m \leq 101≤n,m≤10。
对于 50%50\%50% 的数据,满足 1≤n,m≤1031 \leq n,m \leq 10^31≤n,m≤103。
对于 80%80\%80% 的数据,满足 1≤n,m≤1051 \leq n,m \leq 10^51≤n,m≤105。
对于 100%100\%100% 的数据,满足 1≤n,m≤2×1051 \leq n,m \leq 2\times 10^51≤n,m≤2×105,∣ai∣≤109|a_i| \leq 10^9∣ai∣≤109,1≤l≤r≤n1 \leq l \leq r \leq n1≤l≤r≤n,1≤k≤r−l+11 \leq k \leq r - l + 11≤k≤r−l+1。
1、 值域线段树,可持久化线段树。
cnt[x] 表示 x出现的次数
2、主席树, 就是n颗线段树叠加。 每新扫描一个数,就增加
log(n) + 1 个节点。 这些新节点,和原来的某些节点,构成这颗新的线段树。
3、 求区间 [l, r] 第k小; 利用前缀和。
4、主席树,插入,查询都需要双指针同步搜索
5、离散化
#include
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
using namespace std;
const int N = 2e5 + 10;
int a[N];
int n, m;
vector v;struct node
{int ch[2];int s; // 节点值域中有多少个数
}tr[N * 22];int root[N], idx; // root[k] 表示第k个数(第k个版本)树的根节点 void build(int &x, int l, int r) //建一颗空树
{x = ++idx;if(l == r)return;int m = (l + r) >> 1;build(lc(x), l, m);build(rc(x), m + 1, r);
}void insert(int x, int& y, int l, int r, int v) // x是前一版本的节点指针,y是当前版本的
{y = ++idx; tr[y] = tr[x]; tr[y].s++;if(l == r)return;int m = (l + r) >> 1; //双指针同步搜索if(v <= m)insert(lc(x), lc(y), l, m, v);elseinsert(rc(x), rc(y), m + 1, r, v);
}//在主席树上查询区间 [l, r] 的第k小
//x是前一版本的指针,y是当前版本的指针
int query(int x, int y, int l, int r, int k)
{if(l == r)return l;int m = (l + r) >> 1;int s = tr[lc(y)].s - tr[lc(x)].s; //这一步的意义, sum[r] - sum[l - 1]//比如求区间 [L, R]的第k小。 那么,第R颗线段树表示 前R个数出现次数的情况//那么,第L - 1颗线段树表示 前L - 1个数出现次数的情况//s = tr[lc(y)].s - tr[lc(x)].s, 就表示 区间 [L, R] 一共多少个数了if(k <= s)return query(lc(x), lc(y), l, m, k);elsereturn query(rc(x), rc(y), m + 1, r, k - s);
}void discrete()
{sort(v.begin(), v.end());v.erase(unique(v.begin(), v.end()), v.end());
}int getid(int x) // 返回x的下标
{return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}int main()
{scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++i){scanf("%d", &a[i]);v.push_back(a[i]);}discrete();build(root[0], 1, n);for(int i = 1; i <= n; ++i){insert(root[i - 1], root[i], 1, n, getid(a[i]));}int l, r, k;while(m--){scanf("%d%d%d", &l, &r, &k);int id = query(root[l - 1], root[r], 1, n, k) - 1;printf("%d\n", v[id]);}return 0;
}
下一篇:代码随想录——Dota2参议院