zoukankan      html  css  js  c++  java
  • 整体二分详解

    整体二分的定义:

    在信息学竞赛中,有一部分题可以使用二分的办法来解决。但是当这种题目有多次询问且每次询问我们对每个查询都直接二分,可能会收获一个 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;
    }
    
  • 相关阅读:
    偶遇问题
    上机实践
    知识点摸清
    实用脚本
    实用脚本
    对于问题,要打破砂锅问到底,也要懂得不求甚解——不执着于问题本身
    偶遇问题
    知识点摸清
    偶遇问题
    程序员论坛
  • 原文地址:https://www.cnblogs.com/little-uu/p/14743791.html
Copyright © 2011-2022 走看看