整体二分

随便写一点整体二分的东西。

这个整体二分啊,非常的简单

拿最简单的出来说吧

poj2104

n,m<=100000

给一个长为n的数列a,有m个询问

每次输入l,r,k询问al~ar中第k小的是哪一个。

【solution】

你们可能说主席树。

然而有一个空间只要O(n)的做法,没错,就是整体二分。

那么这个整体二分是什么呢。

首先,我们把询问丢进一个struct 里面

然后我们二分一个答案mid

然后我们O(nlogn)求出每个询问的范围中<=mid的数的个数tot

显然啊,如果这个数量tot>k的话,显然这个询问的答案就<mid

那么我们现在就把所有的询问分成的两半。

分治递归下去做。

完了。时间O(nlogn^2)

是不是很简单啊。

以后出给学妹做,然后学妹写主席树空间被卡,

哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈。

但是注意一点哦,这个过程中询问的顺序一定要保证哦。

否则的话,就乱套了。

poj2104这个代码比较gay,去看后面那个比较好。

#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<math.h>
#define il inline
#define re register
#define lim 1e9
#define lowbit(x) (x&(-x))
using namespace std;
const int N=1000001;
struct data{int u,v;
} a[N];
struct query{int l,r,k,id,tot;
} qu[N],qt[N];
int n,m,c[N],ans[N];
il int cmp(data a,data b){
    return a.v<b.v;
}
il void add(int p,int v){
    for(;p<=n;p+=lowbit(p)) c[p]+=v;
}
il int sum(int p){
    int ans=0;
    for(;p;p-=lowbit(p)) ans+=c[p];
    return ans;
}
il void conquer(int l,int r,int p,int q){
    int L=1,R=n+1,MID;
    while(L<R){
        MID=(L+R)/2;
        if(a[MID].v>=p) R=MID;
        else L=MID+1;
    }
    for(int i=R;i<=n&&a[i].v<=q;i++) add(a[i].u,1);
    for(int i=l;i<=r;i++)
        qu[i].tot=sum(qu[i].r)-sum(qu[i].l-1);
    for(int i=R;i<=n&&a[i].v<=q;i++) add(a[i].u,-1);
}
il void divide(int l,int r,int p,int q){
//    cout<<l<<" "<<r<<" "<<p<<" "<<q<<endl;
    if(p==q){
        for(int i=l;i<=r;i++)
            ans[qu[i].id]=q;
        return;
    }
    int mid=p+(q-p)/2;
    conquer(l,r,p,mid);
    int L=l-1,R=r+1;
    for(int i=l;i<=r;i++){
        if(qu[i].tot>=qu[i].k) qt[++L]=qu[i];
        else{
            qu[i].k-=qu[i].tot;
            qt[--R]=qu[i];
        }
    }
    for(int i=l;i<=r;i++) qu[i]=qt[i];
    if(L>=l) divide(l,L,p,mid);
    if(R<=r) divide(R,r,mid+1,q);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        a[i].u=i;
        scanf("%d",&a[i].v);
    }
    sort(a+1,a+n+1,cmp);a[n+1].v=lim;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&qu[i].l,&qu[i].r,&qu[i].k);
        qu[i].id=i;
    }
    divide(1,m,-lim,lim);
    for(int i=1;i<=m;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}

hdu5412

就是前面一题加一个修改,

这个也不虚,修改就是删掉一个数再加回来。

和询问一样丢进去分治,直接按照数的大小拿去二分。

重要的事情再讲一遍,注意顺序哦。

自认为这个代码写的比较清楚。

#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define il inline
#define re register
#define lim 1000000000
#define lowbit(p) (p&(-p))
using namespace std;
const int N=1000001;
struct edge{int u,v,w,id,tot;
} a[N],d[N],e[N];
int n,m,b[N],M,t[N],c[N],ans[N];
il void add(int p,int v){
    for(;p<=n;p+=lowbit(p))    c[p]+=v;
}
il int sum(int p){
    int ans=0;
    for(;p;p-=lowbit(p)) ans+=c[p];
    return ans;
}
il void divide(int l,int r,int p,int q){
    if(p>q||l>r) return;
    if(p==q){
        for(int i=l;i<=r;i++)
            if(a[i].w) ans[a[i].id]=p;
        return;
    }
    int mid=(p+q)/2;
    for(int i=l;i<=r;i++){
        if(a[i].w) t[i]=sum(a[i].v)-sum(a[i].u-1);
        else if(a[i].u<=mid) add(a[i].id,a[i].v);
    }
    for(int i=l;i<=r;i++){
        if(a[i].u<=mid&&a[i].w==0)
            add(a[i].id,-a[i].v);
    }
    int t1,t2,t3=l-1;t1=t2=0;
    for(int i=l;i<=r;i++){
        if(a[i].w){
            if(a[i].tot+t[i]>=a[i].w) d[++t1]=a[i];
            else{
                a[i].tot+=t[i];e[++t2]=a[i];
            }
        }
        else if(a[i].u<=mid) d[++t1]=a[i];
        else e[++t2]=a[i];
    }
    for(int i=1;i<=t1;i++) a[++t3]=d[i];
    for(int i=1;i<=t2;i++) a[++t3]=e[i];
    divide(l,l+t1-1,p,mid);divide(l+t1,r,mid+1,q);
}
il void init(){
    memset(ans,false,sizeof(ans));
    memset(c,false,sizeof(c));
    memset(t,false,sizeof(t));
    memset(b,false,sizeof(b));
    M=0;
    for(int i=1,x;i<=n;i++){
        scanf("%d",&x);b[i]=x;
        a[++M]=(edge){x,1,0,i,0};
    }
    scanf("%d",&m);
    for(int i=1,s,t,k,tp;i<=m;i++){
        scanf("%d%d%d",&tp,&s,&t);
        if(tp==1){
            a[++M]=(edge){b[s],-1,0,s,0};
            b[s]=t;
            a[++M]=(edge){b[s],1,0,s,0};
        }
        if(tp==2){
            scanf("%d",&k);
            a[++M]=(edge){s,t,k,i,0};
        }
    }
    divide(1,M,1,lim);
    for(int i=1;i<=m;i++)
        if(ans[i]) printf("%d\n",ans[i]);
}
int main(){
    while(scanf("%d",&n)!=EOF) init();
    return 0;
}
时间: 2024-10-15 22:49:46

整体二分的相关文章

CDQ分治与整体二分小结

前言 这是一波强行总结. 下面是一波瞎比比. 这几天做了几道CDQ/整体二分,感觉自己做题速度好慢啊. 很多很显然的东西都看不出来 分治分不出来 打不出来 调不对 上午下午晚上的效率完全不一样啊. 完蛋.jpg 绝望.jpg. 关于CDQ分治 CDQ分治,求的是三维偏序问题都知道的. 求法呢,就是在分治外面先把一维变成有序 然后分治下去,左边(l,mid)关于右边(mid+1,r)就不存在某一维的逆序了,所以只有两维偏序了. 这个时候来一波"树状数组求逆序对"的操作搞一下二维偏序 就可

BZOJ 3110: [Zjoi2013]K大数查询 [整体二分]

有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. N,M<=50000,N,M<=50000a<=b<=N1操作中abs(c)<=N2操作中c<=Maxlongint 之前用树套树抄过一次...然而我并不适合写那玩意儿... 加上时间序的整体二分 普通的整体二分先处理了所有$[l,mid]$的影响因子在计算询问的答案来分组

bzoj1146整体二分+树链剖分+树状数组

其实也没啥好说的 用树状数组可以O(logn)的查询 套一层整体二分就可以做到O(nlngn) 最后用树链剖分让序列上树 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 inline int read() 7 { 8 int x=0,f=1,ch=getchar(); 9 while(ch<

(困难) CF 484E Sign on Fence,整体二分+线段树

Bizon the Champion has recently finished painting his wood fence. The fence consists of a sequence of n panels of 1 meter width and of arbitrary height. The i-th panel's height is hi meters. The adjacent planks follow without a gap between them. Afte

CDQ分治与整体二分总结

Cdq分治和整体二分是两个很奇妙的东西.他们都是通过离线的思想来进行优化,从而更快的求出解. 整体二分通俗的讲就是二分答案,但是它了不起的地方是一下子把所有的答案都二分出来了,从而可以一下子得出所有查询. CDQ分治通俗的讲就是二分查询.通常的做法是把所有的查询分成两半,然后通过递归先计算出左边一半的所有的查询,然后通过这些已知的左半边的值来更新右半边的值.这里,最最重要的思想是通过左半边来更新右半边.更具体一点,就是用左半边的修改来更新右半边的查询. 重要的事情说话三遍: CDQ分治就是通过左

[整体二分]【学习笔记】【更新中】

先小结一下吧 主要为个人理解 整体二分 理解 $zyz:$整体二分是在权值上进行$CDQ$分治 我觉得更像是说$:$整体二分是在答案上进行$CDQ$分治 整体二分是二分答案在数据结构题上的扩展 因为数据结构题二分的答案通常是第几个操作之后,需要进行一些操作(预处理)之后才能判断,所以每次询问二分还不如从前往后暴力找 整体二分可以解决这样的问题 核心就是维护一个$cur$数组保存当前的影响,分治到$[l,r]$时只需要计算$[l,mid]$的影响再与$cur$里的合并就好了 这样分治里的操作就只与

POJ 2104:K-th Number(整体二分)

http://poj.org/problem?id=2104 题意:给出n个数和m个询问求区间第K小. 思路:以前用主席树做过,这次学整体二分来做.整体二分在yr大佬的指点下,终于大概懂了点了.对于二分能够解决的询问,如果有多个,那么如果支持离线处理的话,那么就可以使用整体二分了. 在这题二分可行的答案,根据这个答案,把询问操作丢在左右两个队列里面分别递归继续按这样处理.注释里写的很详细. 1 #include <iostream> 2 #include <cstdlib> 3 #

整体二分初步

部分内容引自myt论文:树状数组延伸和离线优化(CDQ.整体二分和莫队) 大致思路 1.确定答案范围[L,R],mid=L+R>>1;2.算出答案在[L,mid]内的操作对答案在[mid+1,R]内的操作的贡献;3.将答案在[L,mid]内的操作放入队列Q1,solve(Q1,L,mid)将答案在[mid+1,R]内的操作放入Q2,solve(Q2,mid+1,R) 伪代码 divide(hd,tl,l,r){ if(hd>tl) exit if(l==r){ for(i->hd

整体二分初探 两类区间第K大问题 poj2104 &amp; hdu5412

看到好多讲解都把整体二分和$CDQ$分治放到一起讲 不过自己目前还没学会$CDQ$分治 就单独谈谈整体二分好了 先推荐一下$XHR$的 <浅谈数据结构题的几个非经典解法> 整体二分在当中有较为详细的讲解 先来说一下静态第$K$小的整体二分解法 $(POJ2104)$ 题目链接:http://poj.org/problem?id=2104 所谓整体二分 就是利用所有的询问相互独立而把它们$($此题没有修改操作$)$通过二分把它们分治进行处理 不妨先来考虑下一个简单易懂的$O(NlogS)$的排序