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;
    }
    
  • 相关阅读:
    NBUT 1120 Reimu's Teleport (线段树)
    NBUT 1119 Patchouli's Books (STL应用)
    NBUT 1118 Marisa's Affair (排序统计,水)
    NBUT 1117 Kotiya's Incantation(字符输入处理)
    NBUT 1115 Cirno's Trick (水)
    NBUT 1114 Alice's Puppets(排序统计,水)
    188 Best Time to Buy and Sell Stock IV 买卖股票的最佳时机 IV
    187 Repeated DNA Sequences 重复的DNA序列
    179 Largest Number 把数组排成最大的数
    174 Dungeon Game 地下城游戏
  • 原文地址:https://www.cnblogs.com/little-uu/p/14743791.html
Copyright © 2011-2022 走看看