Kth number HDU - 2665 可持久化线段树 主席树

题解

求区间第K大 主席树模版

AC代码

#include 
#include 
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;
}

你可能感兴趣的:(_数据结构_,主席树)