POJ 2104 K-th Number (划分树,主席树写过了,这次是整体二分解法 )

来源:互联网 发布:矩阵迹的性质及证明 编辑:程序博客网 时间:2024/05/21 10:01

还是先描述一下题意:

给出一个长度为n的数列,m次询问区间内的第k大数


对划分树,主席树和整体二分通过这题做了一下比较

划分树  1000ms+

主席树 2000ms+

整体二分 1500ms+

整体二分介于两者之前,对于这题复杂度约莫是O( (n+m)log(n+m)log( Range( ans )  ) )


整体二分这个东西比较奇妙,运用的是离线算法,而主席树和划分树都是在线的

先引用一下2013年许昊然论文-《浅谈数据结构题的几个非经典解法》解释一下整体二分

这里写图片描述

此题整体二分思路:

1.确定答案在l~r这个区间内

2.取二分中值mid,询问所有查询操作在数组中小于等于mid的情况下,有多少个数在查询区间内

3.由此将查询分为两类

   q1: 区间内个数大于等于k

   q2:区间内个数小于k

可以看出q1情况下的查询应该缩小答案,q2情况下的查询应该放大答案,

同时q2情况下记录mid对对答案的影响值cur(有点类似于cdq分治思想)

由此为依据对数组值和查询操作一起进行二分,回到步骤1一直到得到所有答案

此处统计个数用树状数组简洁方便


#include<cstdio>#include<cstring>#include<algorithm>#include<queue>#include<string>#include<iostream>using namespace std;#define INF 0x3f3f3f3fstruct node{    int l,r,k,val;    int cur,index;    int kind;} q[200005],q1[100005],q2[100005];int n,m;int ans[100006];int c[100006];int tmp[200006];void init(){    memset(c,0,sizeof c);    memset(tmp,0,sizeof tmp);    for(int i=1; i<=m+n; i++)    {        q[i].cur=q1[i].cur=q2[i].cur=0;    }}inline int lowbit(int x){    return x&-x;}inline void update(int x,int val){    for(; x<=n; x+=lowbit(x)) c[x]+=val;}inline int query(int x){    int sum=0;    for(; x>0; x-=lowbit(x)) sum+=c[x];    return sum;}void divide(int s,int t,int l,int r){    if(s>t) return ;    if(l==r)    {        for(int i=s; i<=t; i++)            if(q[i].kind==2) ans[q[i].index]=l;        return ;    }    int mid=(l+r)>>1;    int num1=0,num2=0,flag1=0,flag2=0;    for(int i=s; i<=t; i++)    {        if(q[i].kind==1)        {            if(q[i].val<=mid) update(q[i].index,1),q1[num1++]=q[i];            else q2[num2++]=q[i];        }        else if(q[i].kind==2)        {            tmp[i]=query(q[i].r)-query(q[i].l-1);            if(q[i].cur+tmp[i]>=q[i].k) q1[num1++]=q[i],flag1=1;            else q[i].cur+=tmp[i],q2[num2++]=q[i],flag2=1;        }    }    for(int i=s; i<=t; i++)    {        if(q[i].kind==1&&q[i].val<=mid) update(q[i].index,-1);    }    for(int i=0; i<num1; i++) q[s+i]=q1[i];    for(int i=0; i<num2; i++) q[s+num1+i]=q2[i];    if(flag1)  divide(s,s+num1-1,l,mid);    if(flag2)  divide(s+num1,t,mid+1,r);}int main(){    while(scanf("%d%d",&n,&m)!=EOF)    {        init();        int cnt=1;        for(int i=1; i<=n; i++)        {            scanf("%d",&q[cnt].val);            q[cnt].kind=1;            q[cnt].index=i;            cnt++;        }        for(int i=1; i<=m; i++)        {            q[cnt].index=i;            scanf("%d%d%d",&q[cnt].l,&q[cnt].r,&q[cnt].k);            q[cnt].kind=2;            cnt++;        }        divide(1,cnt-1,-INF,INF);        for(int i=1; i<=m; i++) printf("%d\n",ans[i]);    }    return 0;}







0 0
原创粉丝点击