COGS-257-动态排名系统-树状数组+主席树

描述

  • 给定一个长度为N的已知序列A[i](1<=i<=N),要求维护这个序列,能够支持以下两种操作:
  • 1、查询A[i],A[i+1],A[i+2],...,A[j](1<=i<=j<=N)中,升序排列后排名第k的数。
  • 2、修改A[i]的值为j。
分析
  • 用了很长时间理解树状数组+主席树的做法
  • 这里先有一个正常的主席树, 保存无修改时的数据, 然后用树状数组维护一个记录增量的主席树, 维护的信息和普通树状数组一样, 也是是某一段内的数据. 比如修改了位置x的数据, 那么需要更新 x, x+lowbit(x)... 直到刚小于n 的数据
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
         
         
using namespace std;

const int maxnode = 3000000;
const int maxn = 60000 + 10;
const int maxm = 10000 + 10;

struct Question {
	int a, b, c, k;
} qst[maxm];

int label;
int A[maxn], T[maxn];
int root[maxn<<1];
int s[maxnode], lc[maxnode], rc[maxnode];

#define q qst[i]
#define M (L+R>>1)

void build(int& x, int y, int L, int R, int v) {
	x = ++label;
	lc[x] = lc[y], rc[x] = rc[y], s[x] = s[y] + 1;
	if(L == R) return;
	if(v <= M) build(lc[x], lc[y], L, M, v);
	else build(rc[x], rc[y], M+1, R, v);
}

void modify(int& x, int L, int R, int v, int d) {
	if(!x) x = ++label;
	s[x] += d;
	if(L == R) return;
	if(v <= M) modify(lc[x], L, M, v, d);
	else modify(rc[x], M+1, R, v, d);
}

int main() {
	freopen("dynrank.in", "r", stdin);
	freopen("dynrank.out", "w", stdout);
	 
	int kase;
	scanf("%d", &kase);
	while(kase--) {
		label = 0;
		int n, m, tot = 0;
		scanf("%d %d", &n, &m);
		for(int i = 1; i <= n; i++) {
			scanf("%d", &A[i]);
			T[++tot] = A[i];
		}
		char opt[2];
		for(int i = 1; i <= m; i++) {
			scanf("%s", opt);
			if(opt[0] == 'Q') {
				q.k = 0;
				scanf("%d %d %d", &q.a, &q.b, &q.c);
			} else {
				q.k = 1;
				scanf("%d %d", &q.a, &q.b);
				T[++tot] = q.b;
			}
		}
		sort(T+1, T+tot+1);
		tot = unique(T+1, T+tot+1)-T-1;
		for(int i = 1; i <= n; i++)
			A[i] = lower_bound(T+1, T+tot+1, A[i])-T;
		
		// 几个 memset 特别耗费时间, 可以巧妙地换掉 
		memset(root, 0, sizeof(root));
		memset(s, 0, sizeof(s));
		memset(lc, 0, sizeof(lc));
		memset(rc, 0, sizeof(rc));
		
		// 原始主席树占用空间 [n+1, n+n], 记录前缀和
		// 记录增量占用空间 [1, n], 记录某个位置的增量
		for(int i = 1; i <= n; i++)
			build(root[i + n], root[i-1 + n], 1, tot, A[i]);

		for(int i = 1; i <= m; i++)
			if(q.k == 0) {
				vector
         
         
           q1, q2; int L = 1, R = tot, rk = q.c; // 答案区间 [L, R] if(q.a == 1) q1.push_back(root[0]); else q1.push_back(root[q.a-1 + n]); q2.push_back(root[q.b + n]); for(int x = q.a-1; x > 0; x -= (x&-x)) q1.push_back(root[x]); for(int x = q.b; x > 0; x -= (x&-x)) q2.push_back(root[x]); while(L < R) { int ls = 0; for(int x = 0; x < q1.size(); x++) ls -= s[lc[q1[x]]]; for(int x = 0; x < q2.size(); x++) ls += s[lc[q2[x]]]; if(rk <= ls) { for(int x = 0; x < q1.size(); x++) q1[x] = lc[q1[x]]; for(int x = 0; x < q2.size(); x++) q2[x] = lc[q2[x]]; R = M; } else { for(int x = 0; x < q1.size(); x++) q1[x] = rc[q1[x]]; for(int x = 0; x < q2.size(); x++) q2[x] = rc[q2[x]]; L = M + 1, rk -= ls; } } printf("%d\n", T[L]); } else { for(int x = q.a; x <= n; x += (x&-x)) modify(root[x], 1, tot, A[q.a], -1); A[q.a] = lower_bound(T+1, T+tot+1, q.b)-T; for(int x = q.a; x <= n; x += (x&-x)) modify(root[x], 1, tot, A[q.a], 1); } } return 0; } 
         
        
        
       
       
      
      
     
     

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值