POJ 2104 K-th Number (划分樹,主席樹寫過了,這次是整體二分解法 )


還是先描述一下題意:

給出一個長度為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 0x3f3f3f3f

struct 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;
}








注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com