整体二分详解

整体二分的定义:

在信息学竞赛中,有一部分题可以使用二分的办法来解决。但是当这种题目有多次询问且每次询问我们对每个查询都直接二分,可能会收获一个 TLE。这时候我们就会用到整体二分。整体二分的主体思路就是把多个查询一起解决。(所以这是一个离线算法)

可以使用整体二分解决的题目需要满足以下性质:

  1. 询问的答案具有可二分性

  2. 修改对判定答案的贡献互相独立,修改之间互不影响效果

  3. 修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值

  4. 贡献满足交换律,结合律,具有可加性

  5. 题目允许使用离线算法

算法流程:

记[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;
}
posted @ 2021-05-08 10:10  小又又yyyy  阅读(445)  评论(0编辑  收藏  举报