浅谈整体二分

整体二分这一算法出自许昊然同学的2013年信息学国家队论文《浅谈数据结构题的几个非经典算法》。其优点在于简短的代码量,可以在较低的代价下,简化很多需要使用复杂数据结构的问题。
不同于传统的数据结构题的解题方法,整体二分是将所有操作进行二分,然后采用分治的思想来解决问题。
问题能使用整体二分的前提:

1.满足修改操作对询问的贡献独立,修改操作之间互不影响结果。
2.题目没有强制在线。

那么前提1是什么意思呢?就是说,每一个修改,只对询问的结果产生影响,而不影响其他修改操作。
我们考虑对要执行的操作分成两部分,可以发现,在前一半操作中,所有的询问都不会收到后一半的影响,这样我们可以递归处理。后一半中的修改也是和前一半无关的,而所有查询操作则是受到前一半中所有修改操作的影响的。这样,后半部分的问题就简化为了一个先操作再询问的问题,问题也就离线化了。
而我们离线化所需要的时间开销,在许昊然论文中证明如下
这里写图片描述
所以,我们可以以一个log的开销来极大的降低我们的编码难度,将一些需要复杂的高级数据结构问题简化。
伪代码如下:
这里写图片描述
ZOJ-2112 模版题代码

#include <stdio.h>
#include <bits/stdc++.h>

using namespace std;

const int N = 50005;
const int M = 20005;
const int INF = 1e9;
struct Query{
    int id,op,x,y,k;
}q[N+M],q1[N+M],q2[N+M];
int ans[N],c[N],a[N];

void Add(int i,int x){
    for(;i<N;i+=(i&(-i)))
        c[i]+=x;
}

int Sum(int i){
    int res=0;
    for(;i>0;i-=(i&(-i)))
        res+=c[i];
    return res;
}

void solve(int ql,int qr,int l,int r){
    if(ql>qr)
        return ;
    if(l==r){
        for(int i=ql;i<=qr;i++)
            if(q[i].op==2)
                ans[q[i].id]=l;
        return ;
    }

    int m=l+r>>1,f=0,g=0;
    for(int i=ql;i<=qr;i++)
        if(q[i].op==1){
            if(q[i].k<=m){
                Add(q[i].x,q[i].id);
                q1[f++]=q[i];
            }else
                q2[g++]=q[i];
        }else if(q[i].op==2){
            int cnt=Sum(q[i].y)-Sum(q[i].x-1);
            if(q[i].k<=cnt)
                q1[f++]=q[i];
            else{
                q[i].k-=cnt;
                q2[g++]=q[i];
            }
        }

    for(int i=0;i<f;i++)
        if(q1[i].op==1)
            Add(q1[i].x,-q1[i].id);

    memcpy(q+ql,q1,f*sizeof(Query));
    memcpy(q+ql+f,q2,g*sizeof(Query));

    solve(ql,ql+f-1,l,m);
    solve(ql+f,qr,m+1,r);
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n,m,cnt=1,qq=1;
        memset(c,0,sizeof c);
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            q[cnt++]=(Query){1,1,i,i,a[i]};
        }
        char opt;
        int x,y,k;
        for(int i=1;i<=m;i++){
            scanf(" %c %d %d",&opt,&x,&y);
            if(opt=='C'){
                q[cnt++]=(Query){-1,1,x,x,a[x]};
                q[cnt++]=(Query){1,1,x,x,y};
                a[x]=y;
            }else if(opt=='Q'){
                scanf("%d",&k);
                q[cnt++]=(Query){qq++,2,x,y,k};
            }
        }
        solve(1,cnt-1,0,INF);
        for(int i=1;i<qq;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}

整体二分入门题:
Poj2104
Bzoj3110
Hdu5412

下面这个博客代码写的很好,我的代码就是仿照他的学习的。
大神的博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值