题解
求区间第K大 主席树模版
AC代码
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1e5 + 10;
const int MAXM = 2e6 + 10; //理论q*logn 迷之RE尽量开大
int root[MAXN], son[MAXM][2], idx; //历史版本根节点 左右儿子 节点编号
int a[MAXN], val[MAXM]; //原数组 节点信息
vector<int> dz; //离散化
void Update(int old, int &x, int l, int r, int v) //在old版本的基础上插入一个值为v的数 类似可持久化线段树
{
x = ++idx; //x传引用
son[x][0] = son[old][0], son[x][1] = son[old][1], val[x] = val[old] + 1; //复制原有信息 节点值+1
if (l == r)
return;
int m = l + r >> 1;
if (m >= v)
Update(son[x][0], son[x][0], l, m, v);
else
Update(son[x][1], son[x][1], m + 1, r, v);
}
int Query(int old, int &x, int l, int r, int k) //查询[old, x]区间第k大
{
if (l == r)
return l; //找到返回
int m = l + r >> 1;
int lef = val[son[x][0]] - val[son[old][0]]; //左子树前缀和做差
if (k <= lef) //树上二分
return Query(son[old][0], son[x][0], l, m, k); //给old和x的儿子传递给下一层
else
return Query(son[old][1], son[x][1], m + 1, r, k - lef); //减去左子树数量
}
int Dis(int v)
{
return lower_bound(dz.begin(), dz.end(), v) - dz.begin();
}
int main()
{
#ifdef LOCAL
freopen("C:/input.txt", "r", stdin);
#endif
int T;
cin >> T;
while (T--)
{
dz.clear();
dz.push_back(INT_MIN);
idx = 0; //可行??
int N, M;
cin >> N >> M;
for (int i = 1; i <= N; i++)
scanf("%d", &a[i]), dz.push_back(a[i]);
sort(dz.begin(), dz.end());
dz.erase(unique(dz.begin(), dz.end()), dz.end());
for (int i = 1; i <= N; i++)
Update(root[i - 1], root[i], 1, dz.size(), Dis(a[i])); //前缀和形式
for (int i = 0; i < M; i++)
{
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
printf("%d\n", dz[Query(root[l - 1], root[r], 1, dz.size(), k)]); //离散化的转换回来
}
}
return 0;
}