【洛谷P2617】Dynamic Rankings(整体二分)

题目链接

这是一个动态区间第 k k k 大的模板题,主要有两种方法:

  1. 在线: 主席树套树状数组
  2. 离线: 整体二分(更方便)

整体二分的题通常需要满足如下条件:

  1. 单组询问可以二分
  2. 存在高效的数据结构维护修改对询问的影响(像区间修改就不存在)
  3. 题目可以离线做(废话)

对于该题来说,每个询问都可以二分答案,而每次修改的影响也可以利用树状数组维护前缀和的方式实现。

具体做法如下:

  1. 读入并按时间顺序保存所有加点、删点操作和询问操作
  2. 分治: ( q l , q r ) (ql,qr) (ql,qr) 为操作的区间, ( l , r ) (l,r) (l,r) 为答案区间
    l = r l=r l=r 时,所有询问答案为 l l l
    否则,按时间顺序遍历所有操作,并用树状数组维护 [ l , m i d ] [l,mid] [l,mid] 范围内的数有多少个。
    对于加点、删点操作,直接修改树状数组;
    对于询问操作,当答案 < = m i d <=mid <=mid 时把询问放到子任务 [ l , m i d ] [l,mid] [l,mid],否则放到子任务 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]
    所有操作分配完成后,撤回树状数组的修改操作,向下递归。

代码:

#include<bits/stdc++.h>
#define N 100005
#define inf 0x3fffffff
#define rep(i, l, r) for(int i=l; i<=r; ++i)
using namespace std;
int n, m;
int a[N]; // numbers
struct que{ // operations
	int t; // time
	int opt; // 0-insert, 1-delete, 2-query
	int l, r; // range
	int k; // 0-value, 2-rank
}q[N*3], L[N*3], R[N*3];
int cnt;
int ans[N];
int cnt1=0, cnt2=0;
namespace bit{ // binary index tree
	int b[N];
	void add(int pos, int val){
		for(int i=pos; i<=n; i+=i&-i) b[i]+=val; 
	}
	int query(int pos){
		int sum=0;
		for(int i=pos; i; i-=i&-i) sum+=b[i];
		return sum;
	}
}
void solve(int ql, int qr, int l, int r){
	//printf("%d %d  %d %d\n", ql, qr, l, r);
	if(l==r){
		for(int i=ql; i<=qr; ++i) if(q[i].opt==2) ans[q[i].t]=l;
		return;
	}
	int mid=(l+r)/2, cntL=0, cntR=0, tmp;
	rep(i, ql, qr){
		if(q[i].opt==0){ // insert
			if(q[i].k<=mid) L[++cntL]=q[i], bit::add(q[i].l, 1);
			else R[++cntR]=q[i];
			
		}
		else if(q[i].opt==1){ // delete
			if(q[i].k<=mid) L[++cntL]=q[i], bit::add(q[i].l, -1);
			else R[++cntR]=q[i];
		}
		else{ // query
			tmp=bit::query(q[i].r)-bit::query(q[i].l-1);
			//printf(" 2:%d %d %d  %d %d\n",q[i].l,q[i].r,q[i].k,mid, tmp);
			if(tmp<q[i].k) q[i].k-=tmp, R[++cntR]=q[i];
			else L[++cntL]=q[i];
		}
	}
	rep(i, 1, cntL){ // recall
		q[ql+i-1]=L[i];
		if(L[i].opt==0) bit::add(L[i].l, -1);
		else if(L[i].opt==1) bit::add(L[i].l, 1);
	}
	rep(i, 1, cntR){
		q[ql+cntL+i-1]=R[i]; // in order of time
	}
	if(cntL) solve(ql, ql+cntL-1, l, mid);
	if(cntR) solve(ql+cntL, qr, mid+1, r);
}
int main(){
	scanf("%d%d", &n, &m);
	rep(i, 1, n){
		scanf("%d", &a[i]);
		q[++cnt]=(que){0, 0, i, i, a[i]}; // insert
	} 
	rep(i, 1, m){
		char s[2];
		scanf("%s", s);
		if(s[0]=='C'){ // change
			++cnt1;
			int x, y;
			scanf("%d%d", &x, &y);
			q[++cnt]=(que){cnt1, 1, x, x, a[x]}; // delete
			q[++cnt]=(que){cnt1, 0, x, x, y}; // insert
			a[x]=y;
		}
		if(s[0]=='Q'){ // query
			++cnt2;
			int l, r, k;
			scanf("%d%d%d", &l, &r, &k);
			q[++cnt]=(que){cnt2, 2, l, r, k}; // query
		}
	}
	solve(1, cnt, 0, inf);
	rep(i, 1, cnt2) printf("%d\n", ans[i]);
	return 0;
} 
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值