洛谷P2617 Dynamic Rankings 主席树 单点修改 区间查询第 K 大

我们将线段树套在树状数组上,查询前预处理出所有要一起移动的节点编号,并在查询过程中一起将这些节点移到左右子树上。
Code:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 6000000 + 5;
int A[maxn], arr[maxn];
int n, m, cnt;
struct Queries
{
	int c, l, r, k;      
	Queries(int c = 0, int l = 0, int r = 0, int k = 0):c(c), l(l), r(r), k(k) {}
}asks[maxn];
struct Segment_Tree
{
	int lson[maxn * 10], rson[maxn * 10], root[maxn], temp[2][200], count[2], sumv[maxn * 10]; 
	int cnt_Tree;
	inline int lowbit(int t)
	{
		return t & (-t);
	}
	void insert(int l, int r, int pos, int delta, int &o)
	{
		if(!o) o = ++cnt_Tree;
		sumv[o] += delta;
		if(l == r) return;
		int mid = (l + r) >> 1;
		if(pos <= mid) 
		    insert(l, mid, pos, delta, lson[o]);
		else 
			insert(mid + 1, r, pos, delta, rson[o]);
	}
	inline void update(int pos, int val, int delta)
	{
		for(int i = pos;i <= n; i += lowbit(i))
			insert(1, n, val, delta, root[i]);
	}
	int query(int l, int r, int k)
	{
		if(l == r) return l;
		int sum = 0;
		for(int i = 1;i <= count[0]; ++i) sum += sumv[lson[temp[0][i]]];
		for(int i = 1;i <= count[1]; ++i) sum -= sumv[lson[temp[1][i]]];
		int mid = (l + r) >> 1;
		if(k <= sum) {
			for(int i = 1;i <= count[0]; ++i) temp[0][i] = lson[temp[0][i]];
			for(int i = 1;i <= count[1]; ++i) temp[1][i] = lson[temp[1][i]];
			return query(l, mid, k);
		} 
		else
		{
			for(int i = 1;i <= count[0]; ++i) temp[0][i] = rson[temp[0][i]];
			for(int i = 1;i <= count[1]; ++i) temp[1][i] = rson[temp[1][i]];
			return query(mid + 1, r, k - sum);
		}
	}
	inline int Query(int l, int r, int k)
	{
		memset(temp, 0, sizeof(temp));
		count[0] = count[1] = 0;
		for(int i = r;i >= 1;i -= lowbit(i)) 
			temp[0][++count[0]] = root[i];
		for(int i = l - 1;i >= 1;i -= lowbit(i)) 
			temp[1][++count[1]] = root[i];
		return query(1, n, k);
	}
}T;
inline int get(int a) 
{  
    return lower_bound(A + 1, A + 1 + cnt, a) - A; 
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n; ++i) 
	{
		scanf("%d",&arr[i]);
		A[i] = arr[i];
	}
	cnt = n;
	for(int i = 1;i <= m; ++i)
	{
		char opt[3];
		scanf("%s",opt);
		if(opt[0] == 'Q') 
		{
			int a, b, c;
			scanf("%d%d%d",&a,&b,&c);
			asks[i] = Queries(0, a, b, c);
		} 
		if(opt[0] == 'C')
		{
			int a, b;
			scanf("%d%d",&a,&b);
			asks[i] = Queries(1, a, a, b);
			A[++cnt] = b;
		}
	}
	n = cnt;
	sort(A + 1, A + 1 + cnt);
	for(int i = 1;i <= n; ++i)
	{
		int cur_num = get(arr[i]);
		T.update(i, cur_num, 1);
	}
	for(int i = 1;i <= m; ++i)
	{
		if(asks[i].c)
		{
			int origin_num = get(arr[asks[i].l]);
			T.update(asks[i].l, origin_num, -1); 
			int cur_num = get(asks[i].k);
			T.update(asks[i].l, cur_num, 1);
			arr[asks[i].l] = asks[i].k;
		}
		else printf("%d\n", A[T.Query(asks[i].l, asks[i].r, asks[i].k)]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值