浅谈整体二分

来源:互联网 发布:盘石网络 编辑:程序博客网 时间:2024/05/17 21:43

整体二分这一算法出自许昊然同学的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

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

原创粉丝点击