洛谷题单指南-线段树的进阶用法-P2617 Dynamic Rankings
原题链接:https://www.luogu.com.cn/problem/P2617
题意解读:动态求区间第k小问题。
解题思路:树套树的典型应用,具体阐述参考:https://www.cnblogs.com/jcwy/p/18640914
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
struct Op
{
char op;
int l, r, k;
int x, y;
} ops[N];
struct Node
{
int L, R;
int cnt;
} tr[N * 400];
int root[N], idx;
int a[N];
vector<int> b; //用于离散化
vector<int> tmpl, tmpr; //缓存查询的根节点
int n, m;
//获取x离散化之后的值
int lsh(int x)
{
return lower_bound(b.begin(), b.end(), x) - b.begin() + 1;
}
int lowbit(int x)
{
return x & -x;
}
void pushup(int u)
{
tr[u].cnt = tr[tr[u].L].cnt + tr[tr[u].R].cnt;
}
//将根为u的线段树中值val的数量增加add
int update(int u, int l, int r, int val, int num)
{
if(!u) u = ++idx;
if(l == r)
{
tr[u].cnt += num;
return u;
}
int mid = l + r >> 1;
if(val <= mid) tr[u].L = update(tr[u].L, l, mid, val, num);
else tr[u].R = update(tr[u].R, mid + 1, r, val, num);
pushup(u);
return u;
}
//在根为tmpr、tmpl的线段树查询第k小值
//左子树的元素数量=tmpr所有线段树左子树的元素数量-tmpl所有线段树左子树的元素数量
int query(int l, int r, int k)
{
if(l == r) return l;
int res = 0;
for(int i = 0; i < tmpr.size(); i++) res += tr[tr[tmpr[i]].L].cnt;
for(int i = 0; i < tmpl.size(); i++) res -= tr[tr[tmpl[i]].L].cnt;
int mid = l + r >> 1;
if(res >= k)
{
for(int i = 0; i < tmpr.size(); i++) tmpr[i] = tr[tmpr[i]].L;
for(int i = 0; i < tmpl.size(); i++) tmpl[i] = tr[tmpl[i]].L;
return query(l, mid, k);
}
else
{
for(int i = 0; i < tmpr.size(); i++) tmpr[i] = tr[tmpr[i]].R;
for(int i = 0; i < tmpl.size(); i++) tmpl[i] = tr[tmpl[i]].R;
return query(mid + 1, r, k - res);
}
}
//利用树状数组将root[pos]~root[n]线段树中值val的数量加num
void add(int pos, int val, int num)
{
for(int i = pos; i <= n; i += lowbit(i))
root[i] = update(root[i], 1, b.size(), val, num);
}
//利用树状数组在root[l]~root[r]线段树中查询第k小的值
int find_kth(int l, int r, int k)
{
tmpr.clear();
tmpl.clear();
//缓存要查询的root[1]~root[r]的值涉及的线段树根节点
for(int i = r; i; i -= lowbit(i)) tmpr.push_back(root[i]);
//缓存要查询的root[1]~root[l-1]的值涉及的线段树根节点
for(int i = l - 1; i; i -= lowbit(i)) tmpl.push_back(root[i]);
return query(1, b.size(), k);
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
cin >> a[i];
b.push_back(a[i]);
}
for(int i = 1; i <= m; i++)
{
cin >> ops[i].op;
if(ops[i].op == 'Q') cin >> ops[i].l >> ops[i].r >> ops[i].k;
else if(ops[i].op == 'C')
{
cin >> ops[i].x >> ops[i].y;
b.push_back(ops[i].y);
}
}
//排序,去重,用于离散化
sort(b.begin(), b.end());
b.erase(unique(b.begin(), b.end()), b.end());
for(int i = 1; i <= n; i++) add(i, lsh(a[i]), 1);
for(int i = 1; i <= m; i++)
{
if(ops[i].op == 'Q')
{
cout << b[find_kth(ops[i].l, ops[i].r, ops[i].k) - 1] << endl;
}
else if(ops[i].op == 'C')
{
add(ops[i].x, lsh(a[ops[i].x]), -1);
a[ops[i].x] = ops[i].y;
add(ops[i].x, lsh(a[ops[i].x]), 1);
}
}
}
分类:
洛谷官方题单
标签:
树套树
, 树状数组套权值线段树
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?