[学习]整体二分

在?看看整体二分
整体二分是个啥,就是递归进行二分答案的操作,按照当前二分出的区间对询问操作和修改操作进行左右分类。有点类似于归并排序的样子,但是需要用个维护区间的数据结构来维护当前询问区间的区间的查询和修改操作,每次查询完当前区间的操作之后,需要清空之前的修改操作。整体二分可以保证会互相影响的操作可以按照顺序进行。但是本质还是离线算法。
这样执行的操作时间复杂度是 O ( M l o g ( a n s ) l o g ( M ) ) O(M*log(ans)*log(M))

POJ2104 查询区间第K小
需要一个树状数组来维护区间元素个数。

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define ll long long
#define INF 1999122700LL
using namespace std;
const int MAXN=200000;
int lowbit(int x){return x&(-x);}
int n,m,cnt;
int tr[MAXN+4];
void add(int x,int v){
    for(int i=x;i<=n;i+=lowbit(i))tr[i]+=v;
}
int query(int x){
    int ret=0;
    for(int i=x;i;i-=lowbit(i))ret+=tr[i];
    return ret;
}
struct Quc{
    int l,r,k,pos,typ;
    Quc(){}
    Quc(int _l,int _r,int _k,int _p,int _t){
        l=_l;
        r=_r;
        k=_k;
        pos=_p;
        typ=_t;
    }
}pl[MAXN+4],pL[MAXN+4],pR[MAXN+4];
int ans[MAXN+4];
void solve(ll l,ll r,int L,int R){
    if(l>r||L>R)return ;
    if(l==r){
        for(int i=L;i<=R;i++){
            if(!pl[i].typ)continue;
            ans[pl[i].pos]=l;
        }
        return ;
    }
    ll mid=(l+r)>>1;
    int cl=0,cr=0;
    for(int i=L;i<=R;i++){
        if(!pl[i].typ){
            if(pl[i].k<=mid){
                pL[++cl]=pl[i];
                add(pl[i].pos,1);
            }
            else{
                pR[++cr]=pl[i];
            }
        }
        else{
            int delta=query(pl[i].r)-query(pl[i].l-1);
            if(pl[i].k<=delta){
                pL[++cl]=pl[i];
            }
            else{
                pl[i].k-=delta;
                pR[++cr]=pl[i];
            }
        }
    }
    for(int i=1;i<=cl;i++){
        if(!pL[i].typ){
            add(pL[i].pos,-1);
        }
    }
    for(int i=1;i<=cl;i++)pl[L-1+i]=pL[i];
    for(int i=1;i<=cr;i++)pl[L-1+cl+i]=pR[i];
    solve(l,mid,L,L+cl-1);
    solve(mid+1,r,L+cl,R);
}
int w33ha(){
    cnt=0;
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);
        pl[++cnt]=Quc(0,0,x,i,0);
    }
    for(int i=1;i<=m;i++){
        int l,r,k;scanf("%d%d%d",&l,&r,&k);
        pl[++cnt]=Quc(l,r,k,i,1);
    }
    solve(-INF,INF,1,cnt);
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}
int main(){
    while(scanf("%d%d",&n,&m)!=EOF)w33ha();
    return 0;
}

bzoj3110
n n 个格子,开始每个格子都是空的。
m m 个操作。
两种操作,一个查询区间所有元素的第 K K 大,第二个操作是区间的格子内添加数字。
插入的数字c可以变为 I N F c + 1 INF-c+1 ,这样查询的时候就会变成查询第 K K 小了,然后记答案的时候记成 I N F l + 1 INF-l+1 即可
第二个操作可以变为区间加。那么用线段树代替树状数组即可。

#include<bits/stdc++.h>
#define ll long long
#define INF (1LL<<31)-1
using namespace std;
const int MAXN=50000;
int n,m;
struct tree{
    ll tag,w;
}tr[500004];
struct Quc{
    int l,r,pos,typ;
    ll k;
    Quc(){}
    Quc(int _l,int _r,ll _k,int _p,int _t){
        l=_l;
        r=_r;
        k=_k;
        pos=_p;
        typ=_t;
    }
}pl[(MAXN<<1)+4],pL[(MAXN<<1)+4],pR[(MAXN<<1)+4];
int cnt;
ll ans[(MAXN<<1)+4];
void pushdown(int k,int l,int r){
    if(!tr[k].tag)return ;
    ll tag=tr[k].tag;
    tr[k].tag=0;
    int mid=(l+r)>>1;
    tr[k<<1].tag+=tag;
    tr[k<<1|1].tag+=tag;
    tr[k<<1].w+=(mid-l+1)*tag;
    tr[k<<1|1].w+=(r-mid)*tag;
}
void add(int k,int l,int r,int a,int b,int v){
    if(l==a&&r==b){
        tr[k].w+=1LL*(b-a+1)*v;
        tr[k].tag+=v;
        return ;
    }
    pushdown(k,l,r);
    int mid=(l+r)>>1;
    if(b<=mid)add(k<<1,l,mid,a,b,v);
    else if(a>mid)add(k<<1|1,mid+1,r,a,b,v);
    else{
        add(k<<1,l,mid,a,mid,v);
        add(k<<1|1,mid+1,r,mid+1,b,v);
    }
    tr[k].w=tr[k<<1].w+tr[k<<1|1].w;
}
ll query(int k,int l,int r,int a,int b){
    if(l==a&&r==b){
        return tr[k].w;
    }
    pushdown(k,l,r);
    int mid=(l+r)>>1;
    if(b<=mid)return query(k<<1,l,mid,a,b);
    else if(a>mid)return query(k<<1|1,mid+1,r,a,b);
    else{
        return query(k<<1,l,mid,a,mid)+query(k<<1|1,mid+1,r,mid+1,b);
    }
}
void solve(ll l,ll r,int L,int R){
    if(r<l||R<L)return;
    if(l==r){
        for(int i=L;i<=R;i++){
            if(pl[i].typ==2)ans[pl[i].pos]=INF-l+1;
        }
        return ;
    }
    ll mid=(l+r)>>1;
    int cl=0,cr=0;
    for(int i=L;i<=R;i++){
        if(pl[i].typ==1){
            if(pl[i].k<=mid){
                add(1,1,n,pl[i].l,pl[i].r,1);
                pL[++cl]=pl[i];
            }
            else{
                pR[++cr]=pl[i];
            }
        }
        else{
            ll delta=query(1,1,n,pl[i].l,pl[i].r);
            if(pl[i].k<=delta){
                pL[++cl]=pl[i];
            }
            else{
                pl[i].k-=delta;
                pR[++cr]=pl[i];
            }
        }
    }
    for(int i=1;i<=cl;i++){
        if(pL[i].typ==1)add(1,1,n,pL[i].l,pL[i].r,-1);
    }
    for(int i=1;i<=cl;i++)pl[L-1+i]=pL[i];
    for(int i=1;i<=cr;i++)pl[L-1+cl+i]=pR[i];
    solve(l,mid,L,L+cl-1);
    solve(mid+1,r,L+cl,R);
}

int main(){
    scanf("%d%d",&n,&m);
    cnt=0;
    for(int i=1;i<=m;i++){
        int t,a,b,c;
        scanf("%d%d%d%d",&t,&a,&b,&c);
        if(t==2)pl[i]=Quc(a,b,c,++cnt,t);
        else pl[i]=Quc(a,b,INF-c+1,0,t);
    }
    solve(-INF,INF,1,m);
    for(int i=1;i<=cnt;i++)printf("%lld\n",ans[i]);
    return 0;
}
发布了302 篇原创文章 · 获赞 19 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/dxyinme/article/details/99672451