淺談整體二分
阿新 • • 發佈:2018-12-02
思路有些像CDQ,都是離線
整體二分滿足以下性質:
1. 詢問的答案具有可二分性
2. 修改對判定答案的貢獻相對獨立,修改之間互不影響效果
3. 修改如果對判定答案有貢獻,則貢獻為一確定的與判定標準無關的值
4. 貢獻滿足交換律,結合律,具有可加性
5. 題目允許離線操作
時間複雜度方面還要滿足整體二分內部不能與序列的總長度相關,而要與當前的序列的長度相關
————————————————————————————————————————————————————————
例1:靜態區間第K小
二分了答案,標記0/1後,(0表示小於等於mid,1表示大於mid),字首和一下,然後再判斷分解,繼續遞迴
但是字首和不能與序列總長度有關
然而可以發現一個性質:原來比之前中的大的,肯定比中的大,所以只用在原來的基礎上修改沒一個數就好了
所以果斷上個樹狀陣列
#include<cstdio> #include<algorithm> using namespace std; const int N=1e6+5; int n,m,ql[N],qr[N],qk[N],id[N],p,n1,n2,c[N],ans[N],t1[N],t2[N]; struct A{int id,x; }a[N]; bool cmp(A i,A j){ return i.x<j.x; } inline void add(int x,int val) { for(int i=x;i<=n;i+=i&-i) c[i]+=val; } inline int sum(int x) { int ret=0; for(int i=x;i;i-=i&-i) ret+=c[i]; return ret; } void work(int l,int r,int ll,int rr) { if(l>r) return; int mid=ll+rr>>1; while(p+1<=n&&a[p+1].x<=mid) p++,add(a[p].id,1); while(p&&a[p].x>mid) add(a[p].id,-1),p--; n1=n2=l; for(int i=l;i<=r;i++) if(sum(qr[id[i]])-sum(ql[id[i]]-1)>=qk[id[i]]) ans[id[i]]=mid,t1[n1++]=id[i]; else t2[n2++]=id[i]; for(int i=l;i<n1;i++) id[i]=t1[i]; for(int i=n1;i<=r;i++) id[i]=t2[i-n1+l]; if(ll==rr) return; work(l,n1-1,ll,mid),work(n1,r,mid+1,rr); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i].x),a[i].id=i; sort(a+1,a+n+1,cmp); for(int i=1;i<=m;i++) scanf("%d%d%d",&ql[i],&qr[i],&qk[i]),id[i]=i; work(1,n,a[1].x,a[n].x); for(int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }
例2:動態區間第K小
還沒想清楚,留個坑