整体二分

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 }
BZOJ1901

 

[BZOJ2527][POI2011]Meteors

显然我们可以对每个询问二分,然后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 }
BZOJ2527

 

[BZOJ3110][ZJOI2013]K大数查询

同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 }
BZOJ3110

 

[BZOJ2738]矩阵乘法

同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 }
BZOJ2738

 

以上是关于整体二分的主要内容,如果未能解决你的问题,请参考以下文章

整体二分

整体二分QAQ

POJ2104 K-th Number(整体二分)

整体二分初步

静态区间第k小 - 整体二分

bzoj 3110 [Zjoi2013]K大数查询 整体二分