【洛谷P2617】Dynamic Rankings(整体二分)
这是一个动态区间第k大的模板题,主要有两种方法:
- 在线: 主席树套树状数组
- 离线: 整体二分(更方便)
整体二分的题通常需要满足如下条件:
- 单组询问可以二分
- 存在高效的数据结构维护修改对询问的影响(像区间修改就不存在)
- 题目可以离线做(废话)
对于该题来说,每个询问都可以二分答案,而每次修改的影响也可以利用树状数组维护前缀和的方式实现。
具体做法如下:
- 读入并按时间顺序保存所有加点、删点操作和询问操作
- 分治:(ql,qr)为操作的区间,(l,r)为答案区间
当l=r时,所有询问答案为l。
否则,按时间顺序遍历所有操作,并用树状数组维护[l,mid]范围内的数有多少个。
对于加点、删点操作,直接修改树状数组;
对于询问操作,当答案<=mid时把询问放到子任务[l,mid],否则放到子任务[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;
}