整体二分的定义:
在信息学竞赛中,有一部分题可以使用二分的办法来解决。但是当这种题目有多次询问且每次询问我们对每个查询都直接二分,可能会收获一个 TLE。这时候我们就会用到整体二分。整体二分的主体思路就是把多个查询一起解决。(所以这是一个离线算法)
可以使用整体二分解决的题目需要满足以下性质:
-
询问的答案具有可二分性
-
修改对判定答案的贡献互相独立,修改之间互不影响效果
-
修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值
-
贡献满足交换律,结合律,具有可加性
-
题目允许使用离线算法
算法流程:
记[l,r]为答案的值域,[L,R]为答案的定义域
-
我们首先把所有操作按照时间的顺序存入数组中,进行分治
-
在每一层分治中,统计当前查询的答案和mid之间的关系。
-
根据查询出来的答案和mid间的关系(小于等于mid和大于mid)将当前处理的操作序列分为(q_1)和(q_2)两份,并分别递归处理。
-
当l == r时找到了答案
例题:Dynamic Rankings
可以去猜测所有询问的答案都是mid,然后去依次验证每个询问的答案应该是小于等于mid的还是大于mid的,并将询问分为两个部分(不大于/大于),对于每个部分继续二分。注意:如果一个询问的答案是大于mid的,则在将其划至右侧前需更新它的k,即,如果当前数列中小于等于mid的数有t个,则将询问划分后实际是在右区间询问第k-t小数,看代码应该会清晰很多
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
int read(){
int x = 1,a = 0;char ch = getchar();
while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
return x*a;
}
const int maxn = 1e6+10,inf = 1e9+7;
int n,m,a[maxn],ans[maxn];
struct node{
int x,y,k,id,op;
}q[maxn<<1],q1[maxn<<1],q2[maxn<<1];
int sum[maxn];
void add(int x,int y){
for (int i = x;i <= n;i += i&-i) sum[i] += y;
}
int query(int x){
int res = 0;
for (int i = x;i;i -= i&-i) res += sum[i];
return res;
}
void solve(int ql,int qr,int l,int r){
if (ql > qr||l > r) return;
if (l == r){
for (int i = ql;i <= qr;i++) if (q[i].op == 2) ans[q[i].id] = l;
return;
}
int cnt1 = 0,cnt2 = 0,mid = (l+r >> 1);
for (int i = ql;i <= qr;i++){
if (q[i].op != 2){
if (q[i].x <= mid) add(q[i].id,q[i].op),q1[++cnt1] = q[i];
else q2[++cnt2] = q[i];
}
else{
int res = query(q[i].y)-query(q[i].x-1);
if (res >= q[i].k) q1[++cnt1] = q[i];
else q[i].k -= res,q2[++cnt2] = q[i];
}
}
for (int i = 1;i <= cnt1;i++) if (q1[i].op != 2) add(q1[i].id,-q1[i].op);
for (int i = 1;i <= cnt1;i++) q[i+ql-1] = q1[i];
for (int i = 1;i <= cnt2;i++) q[i+ql+cnt1-1] = q2[i];
solve(ql,ql+cnt1-1,l,mid),solve(ql+cnt1,qr,mid+1,r);
}
int cnt;
int main(){
n = read(),m = read();
for (int i = 1;i <= n;i++){
a[i] = read();
q[++cnt] = node{a[i],0,0,i,1};
}
int num = 0;
for (int i = 1;i <= m;i++){
char op[5];scanf ("%s",op);
if (op[0] == 'Q'){
int l = read(),r = read(),k = read();
q[++cnt] = node{l,r,k,++num,2};
}
else{
int x = read(),k = read();
q[++cnt] = node{a[x],0,0,x,-1};
q[++cnt] = node{a[x] = k,0,0,x,1};
}
}
solve(1,cnt,0,inf);
for (int i = 1;i <= num;i++) cout<<ans[i]<<endl;
return 0;
}