整体二分
Posted hocriser
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了整体二分相关的知识,希望对你有一定的参考价值。
整体二分,就是对答案(权值)做CDQ分治。
有些问题会给出一些修改和一些询问,当可以通过二分后线性判定回答询问时,我们就可以将所有修改和询问放在一起二分,复杂度一般会将一个O(n)级别优化掉,这就是整体二分。
一般配合树状数组、线段树等数据结构,来替代树套树、KD-Tree等代码量和常数都较大的方法。
[BZOJ1901][Zju2112] Dynamic Rankings
动态区间第k小是整体二分最经典的应用。
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 using namespace std; 5 6 const int N=30010; 7 char ch; 8 int n,m,x,y,z,tot,cnt,a[N],tmp[N],ans[N],c[N]; 9 struct P{ int x,y,z,op,id; }q[N],q1[N],q2[N]; 10 11 void add(int x,int k){ for (; x<=n; x+=x&-x) c[x]+=k; } 12 int que(int x){ int res=0; for (; x; x-=x&-x) res+=c[x]; return res; } 13 14 void solve(int st,int ed,int l,int r){ 15 if (st>ed) return; 16 if (l==r){ rep(i,st,ed) ans[q[i].id]=l; return; } 17 int mid=(l+r)>>1,tot1=0,tot2=0; 18 rep(i,st,ed){ 19 if (q[i].op==1){ 20 if (q[i].y<=mid) add(q[i].x,1),q1[++tot1]=q[i]; else q2[++tot2]=q[i]; 21 } 22 if (q[i].op==2){ 23 if (q[i].y<=mid) add(q[i].x,-1),q1[++tot1]=q[i]; else q2[++tot2]=q[i]; 24 } 25 if (q[i].op==3){ 26 int t=que(q[i].y)-que(q[i].x-1); 27 if (q[i].z<=t) q1[++tot1]=q[i]; else q[i].z-=t,q2[++tot2]=q[i]; 28 } 29 } 30 rep(i,1,tot1){ 31 if (q1[i].op==1) add(q1[i].x,-1); 32 if (q1[i].op==2) add(q1[i].x,1); 33 } 34 rep(i,1,tot1) q[st+i-1]=q1[i]; 35 rep(i,1,tot2) q[st+tot1+i-1]=q2[i]; 36 solve(st,st+tot1-1,l,mid); 37 solve(st+tot1,ed,mid+1,r); 38 } 39 40 int main(){ 41 freopen("bzoj1901.in","r",stdin); 42 freopen("bzoj1901.out","w",stdout); 43 scanf("%d%d",&n,&m); 44 rep(i,1,n) scanf("%d",&a[i]),q[++tot]=(P){i,a[i],0,1,0}; 45 rep(i,1,m){ 46 scanf(" %c",&ch); 47 if (ch==‘Q‘) scanf("%d%d%d",&x,&y,&z),q[++tot]=(P){x,y,z,3,++cnt}; 48 else scanf("%d%d",&x,&y),q[++tot]=(P){x,a[x],0,2,0},a[x]=y,q[++tot]=(P){x,y,0,1,0}; 49 } 50 solve(1,tot,0,1e9); 51 rep(i,1,cnt) printf("%d ",ans[i]); 52 return 0; 53 }
显然我们可以对每个询问二分,然后O(n)判定。考虑整体二分,用now标记当前处于的是哪个位置的状态,每次暴力调整,均摊是O(nlogn)的。
1 #include<cstdio> 2 #include<vector> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=300010,inf=1e9; 9 ll c[N]; 10 int n,m,l,r,k,Q,tot,now,co[N],d[N],a[N],a1[N],a2[N],ans[N]; 11 struct P{ int l,r,k; }q[N]; 12 vector<int>ve[N]; 13 14 void add(int x,int k){ for (; x<=m; x+=x&-x) c[x]+=k; } 15 ll que(int x){ ll res=0; for (; x; x-=x&-x) res+=c[x]; return res; } 16 17 void work(int x,int k){ 18 add(q[x].l,k*q[x].k),add(q[x].r+1,-k*q[x].k); 19 if (q[x].l>q[x].r) add(1,k*q[x].k); 20 } 21 22 void solve(int st,int ed,int l,int r){ 23 if (st>ed) return; 24 if (l==r){ rep(i,st,ed) ans[a[i]]=l; return; } 25 int mid=(l+r)>>1; 26 while (now<mid) work(++now,1); 27 while (now>mid) work(now--,-1); 28 int tot1=0,tot2=0; 29 rep(i,st,ed){ 30 int en=ve[a[i]].size()-1; ll res=0; 31 rep(j,0,en){ 32 res+=que(ve[a[i]][j]); 33 if (res>=d[a[i]]) break; 34 } 35 if (res>=d[a[i]]) a1[++tot1]=a[i]; else a2[++tot2]=a[i]; 36 } 37 rep(i,1,tot1) a[st+i-1]=a1[i]; 38 rep(i,1,tot2) a[st+tot1+i-1]=a2[i]; 39 solve(st,st+tot1-1,l,mid); 40 solve(st+tot1,ed,mid+1,r); 41 } 42 43 int main(){ 44 freopen("bzoj2527.in","r",stdin); 45 freopen("bzoj2527.out","w",stdout); 46 scanf("%d%d",&n,&m); 47 rep(i,1,m) scanf("%d",&co[i]),ve[co[i]].push_back(i); 48 rep(i,1,n) scanf("%d",&d[i]),a[i]=i; 49 scanf("%d",&Q); 50 rep(i,1,Q) scanf("%d%d%d",&l,&r,&k),q[i]=(P){l,r,k}; 51 q[Q+1]=(P){1,m,inf}; 52 solve(1,n,1,Q+1); 53 rep(i,1,n) if (ans[i]==Q+1) puts("NIE"); else printf("%d ",ans[i]); 54 return 0; 55 }
同BZOJ1901,注意树状数组区间修改单点查询的方法。
http://www.cnblogs.com/RabbitHu/p/BIT.html
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 typedef long long ll; 5 using namespace std; 6 7 const int N=100010; 8 ll c1[N],c2[N]; 9 int n,m,op,l,r,k,cnt,tot,ans[N]; 10 struct P{ int l,r,k,op,id; }q[N],q1[N],q2[N]; 11 12 void add(int x,int k){ for (int i=x; i<=n; i+=i&-i) c1[i]+=k,c2[i]+=1ll*x*k; } 13 14 ll que(int x){ 15 ll res1=0,res2=0; 16 for (int i=x; i; i-=i&-i) res1+=c1[i],res2+=c2[i]; 17 return (x+1)*res1-res2; 18 } 19 20 void solve(int st,int ed,int l,int r){ 21 if (st>ed) return; 22 if (l==r){ rep(i,st,ed) ans[q[i].id]=l; return; } 23 int mid=(l+r+1)>>1,tot1=0,tot2=0; 24 rep(i,st,ed){ 25 if (q[i].op==1){ 26 if (q[i].k>=mid) add(q[i].l,1),add(q[i].r+1,-1),q2[++tot2]=q[i]; 27 else q1[++tot1]=q[i]; 28 }else{ 29 int t=que(q[i].r)-que(q[i].l-1); 30 if (q[i].k<=t) q2[++tot2]=q[i]; 31 else q[i].k-=t,q1[++tot1]=q[i]; 32 } 33 } 34 rep(i,1,tot2) if (q2[i].op==1) add(q2[i].l,-1),add(q2[i].r+1,1); 35 rep(i,1,tot1) q[st+i-1]=q1[i]; 36 rep(i,1,tot2) q[st+tot1+i-1]=q2[i]; 37 solve(st,st+tot1-1,l,mid-1); 38 solve(st+tot1,ed,mid,r); 39 } 40 41 int main(){ 42 freopen("bzoj3110.in","r",stdin); 43 freopen("bzoj3110.out","w",stdout); 44 scanf("%d%d",&n,&m); 45 rep(i,1,m) scanf("%d%d%d%d",&op,&l,&r,&k),q[++tot]=(P){l,r,k,op,(op==1)?0:++cnt}; 46 solve(1,m,-n,n); 47 rep(i,1,cnt) printf("%d ",ans[i]); 48 return 0; 49 }
同BZOJ1901,改成二维树状数组即可。
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 using namespace std; 5 6 const int N=510,M=320010; 7 int n,Q,mx,tot,x,x1,y1,x2,y2,k,cnt,ans[M],c[N][N]; 8 struct P{ int x1,y1,x2,y2,k,op,id; }q[M],q1[M],q2[M]; 9 10 void add(int x,int y,int k){ 11 for (int i=x; i<=n; i+=i&-i) 12 for (int j=y; j<=n; j+=j&-j) c[i][j]+=k; 13 } 14 15 int que(int x,int y){ 16 int res=0; 17 for (int i=x; i; i-=i&-i) 18 for (int j=y; j; j-=j&-j) res+=c[i][j]; 19 return res; 20 } 21 22 void solve(int st,int ed,int l,int r){ 23 if (st>ed) return; 24 if (l==r){ rep(i,st,ed) ans[q[i].id]=l; return; } 25 int mid=(l+r)>>1,tot1=0,tot2=0; 26 rep(i,st,ed) if (q[i].op==1){ 27 if (q[i].x2<=mid) add(q[i].x1,q[i].y1,1),q1[++tot1]=q[i]; 28 else q2[++tot2]=q[i]; 29 }else{ 30 int t=que(q[i].x2,q[i].y2)-que(q[i].x1-1,q[i].y2)-que(q[i].x2,q[i].y1-1)+que(q[i].x1-1,q[i].y1-1); 31 if (q[i].k<=t) q1[++tot1]=q[i]; else q[i].k-=t,q2[++tot2]=q[i]; 32 } 33 rep(i,1,tot1) if (q1[i].op==1) add(q1[i].x1,q1[i].y1,-1); 34 rep(i,1,tot1) q[st+i-1]=q1[i]; 35 rep(i,1,tot2) q[st+tot1+i-1]=q2[i]; 36 solve(st,st+tot1-1,l,mid); solve(st+tot1,ed,mid+1,r); 37 } 38 39 int main(){ 40 freopen("bzoj2738.in","r",stdin); 41 freopen("bzoj2738.out","w",stdout); 42 scanf("%d%d",&n,&Q); int mx=0; 43 rep(i,1,n) rep(j,1,n) scanf("%d",&x),q[++tot]=(P){i,j,x,0,0,1,0},mx=max(mx,x); 44 rep(i,1,Q) scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&k),q[++tot]=(P){x1,y1,x2,y2,k,2,++cnt}; 45 solve(1,tot,0,mx); 46 rep(i,1,cnt) printf("%d ",ans[i]); 47 return 0; 48 }
以上是关于整体二分的主要内容,如果未能解决你的问题,请参考以下文章