POJ 2104 K-th Number (劃分樹,主席樹寫過了,這次是整體二分解法 )
阿新 • • 發佈:2019-02-19
還是先描述一下題意:
給出一個長度為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; }