整体二分

鬼知道为什么这一个sb专题我咕了这么久

整体的二分->整体二分

众所周知,做一些题的时候二分答案时间复杂度是$O(nlogn)$的,但当询问次数也是$O(n)$的时候时间复杂度就是$O(n^2logn)$的,GG

整体二分就是在每次二分询问的操作本质差别不大时,把所有操作用一次二分求出,这样时间复杂度就还是$O(nlogn)$的;

一般都会套一些数据结构在里面处理询问,最常用的是树状数组,加起来时间复杂度就是$O(nlog^2n)$的;

跟cdq分治有一定区别,cdq分治是把问题转化为偏序问题然后二分分治,问题性质要满足左右不会互相影响;

而整体二分更像是在二分答案的同时分治操作,根据操作是否满足二分出来的两个值域来向下分治;

整体二分主要适用于一些数据结构题,但cdq的相同点是都需要离线,所以不适用于强制在线的问题。

例题

(都挺水的)

【POJ2104】Kth Number

静态区间K小,模板题

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 #define eps 1e-9
 9 #define lb(x) (x&-x)
10 using namespace std;
11 typedef long long ll;
12 typedef double db;
13 struct task{
14     int op,id,x,y,v;
15     task(){}
16     task(int _op,int _id,int _x,int _y,int _v){
17         op=_op,id=_id,x=_x,y=_y,v=_v;
18     }
19 }t[500001],t1[500001],t2[500001];
20 int n,m,mi=inf,mx=-inf,x,y,k,cnt=0,ans[500001],tr[500001];
21 void add(int x,int v){
22     for(;x<=n;x+=lb(x)){
23         tr[x]+=v;
24     }
25 }
26 int query(int x){
27     int ret=0;
28     for(;x;x-=lb(x)){
29         ret+=tr[x];
30     }
31     return ret;
32 }
33 void solve(int ql,int qr,int l,int r){
34     if(ql>qr||l>r)return;
35     if(ql==qr){
36         for(int i=l;i<=r;i++){
37             if(t[i].op)ans[t[i].id]=ql;
38         }
39         return;
40     }
41     int mid=(ql+qr)/2,cnt1=0,cnt2=0;
42     for(int i=l;i<=r;i++){
43         if(t[i].op){
44             int nw=query(t[i].y)-query(t[i].x-1);
45             if(nw>=t[i].v)t1[++cnt1]=t[i];
46             else{
47                 t[i].v-=nw;
48                 t2[++cnt2]=t[i];
49             }
50         }else{
51             if(t[i].v<=mid){
52                 t1[++cnt1]=t[i];
53                 add(t[i].id,t[i].x);
54             }else t2[++cnt2]=t[i];
55         }
56     }
57     for(int i=1;i<=cnt1;i++){
58         if(!t1[i].op){
59             add(t1[i].id,-t1[i].x);
60         }
61     }
62     for(int i=1;i<=cnt1;i++)t[i+l-1]=t1[i];
63     for(int i=1;i<=cnt2;i++)t[i+l+cnt1-1]=t2[i];
64     solve(ql,mid,l,l+cnt1-1);
65     solve(mid+1,qr,l+cnt1,r);
66 }
67 int main(){
68     scanf("%d%d",&n,&m);
69     for(int i=1;i<=n;i++){
70         scanf("%d",&x);
71         mi=min(mi,x);
72         mx=max(mx,x);
73         t[++cnt]=task(0,i,1,0,x);
74     }
75     for(int i=1;i<=m;i++){
76         scanf("%d%d%d",&x,&y,&k);
77         t[++cnt]=task(1,i,x,y,k);
78     }
79     solve(mi,mx,1,cnt);
80     for(int i=1;i<=m;i++){
81         printf("%d\n",ans[i]);
82     }
83     return 0;
84 }

【BZOJ1901】Zju2112 Dynamic Rankings

带修改区间K小,依然是模板题,但是比树套树好写多了

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 #define eps 1e-9
 9 #define lb(x) (x&-x)
10 using namespace std;
11 typedef long long ll;
12 typedef double db;
13 struct task{
14     int op,id,x,y,v;
15     task(){}
16     task(int _op,int _id,int _x,int _y,int _v){
17         op=_op,id=_id,x=_x,y=_y,v=_v;
18     }
19 }t[500001],t1[500001],t2[500001];
20 int n,m,mi=inf,mx=-inf,x,y,k,totq=0,cnt=0,num[500001],ans[500001],tr[500001];
21 char op[5]; 
22 void add(int x,int v){
23     for(;x<=n;x+=lb(x)){
24         tr[x]+=v;
25     }
26 }
27 int query(int x){
28     int ret=0;
29     for(;x;x-=lb(x)){
30         ret+=tr[x];
31     }
32     return ret;
33 }
34 void solve(int ql,int qr,int l,int r){
35     if(ql>qr||l>r)return;
36     if(ql==qr){
37         for(int i=l;i<=r;i++){
38             if(t[i].op)ans[t[i].id]=ql;
39         }
40         return;
41     }
42     int mid=(ql+qr)/2,cnt1=0,cnt2=0;
43     for(int i=l;i<=r;i++){
44         if(t[i].op){
45             int nw=query(t[i].y)-query(t[i].x-1);
46             if(nw>=t[i].v)t1[++cnt1]=t[i];
47             else{
48                 t[i].v-=nw;
49                 t2[++cnt2]=t[i];
50             }
51         }else{
52             if(t[i].v<=mid){
53                 t1[++cnt1]=t[i];
54                 add(t[i].id,t[i].x);
55             }else t2[++cnt2]=t[i];
56         }
57     }
58     for(int i=1;i<=cnt1;i++){
59         if(!t1[i].op){
60             add(t1[i].id,-t1[i].x);
61         }
62     }
63     for(int i=1;i<=cnt1;i++)t[i+l-1]=t1[i];
64     for(int i=1;i<=cnt2;i++)t[i+l+cnt1-1]=t2[i];
65     solve(ql,mid,l,l+cnt1-1);
66     solve(mid+1,qr,l+cnt1,r);
67 }
68 int main(){
69     scanf("%d%d",&n,&m);
70     for(int i=1;i<=n;i++){
71         scanf("%d",&x);
72         mi=min(mi,x);
73         mx=max(mx,x);
74         num[i]=x;
75         t[++cnt]=task(0,i,1,0,x);
76     }
77     for(int i=1;i<=m;i++){
78         scanf("%s",op);
79         if(op[0]=='Q'){
80             scanf("%d%d%d",&x,&y,&k);
81             t[++cnt]=task(1,++totq,x,y,k);
82         }else{
83             scanf("%d%d",&x,&y);
84             mi=min(mi,y);
85             mx=max(mx,y);
86             t[++cnt]=task(0,x,-1,0,num[x]);
87             t[++cnt]=task(0,x,1,0,y);
88             num[x]=y;
89         }
90     }
91     solve(mi,mx,1,cnt);
92     for(int i=1;i<=totq;i++){
93         printf("%d\n",ans[i]);
94     }
95     return 0;
96 }

【BZOJ2738】矩阵乘法

把第一题改成二维树状数组

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<queue>
 7 #define inf 2147483647
 8 #define eps 1e-9
 9 #define lb(x) (x&-x)
10 using namespace std;
11 typedef long long ll;
12 typedef double db;
13 struct task{
14     int id,x,y,xx,yy,v;
15     task(){}
16     task(int _id,int _x,int _y,int _xx,int _yy,int _v){
17         id=_id,x=_x,y=_y,xx=_xx,yy=_yy,v=_v;
18     }
19 }t[500001],t1[500001],t2[500001];
20 int n,m,mi=inf,mx=-inf,x,y,xx,yy,k,cnt=0,ans[500001],tr[501][501];
21 void add(int x,int y,int v){
22     for(int i=x;i<=n;i+=lb(i)){
23         for(int j=y;j<=n;j+=lb(j)){
24             tr[i][j]+=v;
25         }
26     }
27 }
28 int query(int x,int y){
29     int ret=0;
30     for(int i=x;i;i-=lb(i)){
31         for(int j=y;j;j-=lb(j)){
32             ret+=tr[i][j];
33         }
34     }
35     return ret;
36 }
37 void solve(int ql,int qr,int l,int r){
38     if(ql>qr||l>r)return;
39     if(ql==qr){
40         for(int i=l;i<=r;i++){
41             if(t[i].id)ans[t[i].id]=ql;
42         }
43         return;
44     }
45     int mid=(ql+qr)/2,cnt1=0,cnt2=0;
46     for(int i=l;i<=r;i++){
47         if(t[i].id){
48             int nw=query(t[i].xx,t[i].yy)-query(t[i].x-1,t[i].yy)-query(t[i].xx,t[i].y-1)+query(t[i].x-1,t[i].y-1);
49             if(nw>=t[i].v)t1[++cnt1]=t[i];
50             else{
51                 t[i].v-=nw;
52                 t2[++cnt2]=t[i];
53             }
54         }else{
55             if(t[i].v<=mid){
56                 add(t[i].x,t[i].y,1);
57                 t1[++cnt1]=t[i];
58             }else t2[++cnt2]=t[i];
59         }
60     }
61     for(int i=1;i<=cnt1;i++){
62         if(!t1[i].id){
63             add(t1[i].x,t1[i].y,-1);
64         }
65     }
66     for(int i=1;i<=cnt1;i++)t[i+l-1]=t1[i];
67     for(int i=1;i<=cnt2;i++)t[i+l+cnt1-1]=t2[i];
68     solve(ql,mid,l,l+cnt1-1);
69     solve(mid+1,qr,l+cnt1,r);
70 }
71 int main(){
72     scanf("%d%d",&n,&m);
73     for(int i=1;i<=n;i++){
74         for(int j=1;j<=n;j++){
75             scanf("%d",&x);
76             t[++cnt]=task(0,i,j,0,0,x);
77             mi=min(mi,x);
78             mx=max(mx,x);
79         }
80     }
81     for(int i=1;i<=m;i++){
82         scanf("%d%d%d%d%d",&x,&y,&xx,&yy,&k);
83         t[++cnt]=task(i,x,y,xx,yy,k);
84     }
85     solve(mi,mx,1,cnt);
86     for(int i=1;i<=m;i++){
87         printf("%d\n",ans[i]);
88     }
89     return 0;
90 }

【BZOJ2527】【POI2011】Meteors

环上同一个国家的空间站用链表连起来,每次看当前二分区间内陨石雨下完能否收集到足够的陨石,在最后加上一个量为inf的陨石雨用来判断能否完成,整体二分即可

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<cmath>
  6 #include<queue>
  7 #define inf 1000000000000000
  8 #define eps 1e-9
  9 #define lb(x) (x&-x)
 10 using namespace std;
 11 typedef long long ll;
 12 typedef double db;
 13 struct edge{
 14     int v,next;
 15 }a[300001];
 16 struct task{
 17     int l,r;
 18     ll v;
 19     task(){}
 20     task(int _l,int _r,ll _v){
 21         l=_l,r=_r,v=_v;
 22     }
 23 }t[300002];
 24 int n,m,k,x,tot=0,id[300001],ans[300001],head[300001],t1[300001],t2[300001];
 25 ll tr[300001],nw[300001],ad[300001],num[300001];
 26 void add(int u,int v){
 27     a[++tot].v=v;
 28     a[tot].next=head[u];
 29     head[u]=tot;
 30 }
 31 void add(int x,ll v){
 32     for(;x<=m;x+=lb(x)){
 33         tr[x]+=v;
 34     }
 35 }
 36 ll query(int x){
 37     ll ret=0;
 38     for(;x;x-=lb(x)){
 39         ret+=tr[x];
 40     }
 41     return ret;
 42 }
 43 void solve(int ql,int qr,int l,int r){
 44     if(ql>qr||l>r)return;
 45     if(ql==qr){
 46         for(int i=l;i<=r;i++){
 47             ans[id[i]]=ql;
 48         }
 49         return;
 50     }
 51     int mid=(ql+qr)/2,cnt1=0,cnt2=0;
 52     for(int i=ql;i<=mid;i++){
 53         if(t[i].l<=t[i].r){
 54             add(t[i].l,t[i].v);
 55             add(t[i].r+1,-t[i].v);
 56         }else{
 57             add(1,t[i].v);
 58             add(t[i].r+1,-t[i].v);
 59             add(t[i].l,t[i].v);
 60         }
 61     }
 62     for(int i=l;i<=r;i++){
 63         ad[id[i]]=0;
 64         for(int tmp=head[id[i]];tmp!=-1;tmp=a[tmp].next){
 65             int v=a[tmp].v;
 66             ad[id[i]]+=query(v);
 67             if(ad[id[i]]+nw[id[i]]>num[id[i]])break;
 68         }
 69         if(ad[id[i]]+nw[id[i]]>=num[id[i]])t1[++cnt1]=id[i];
 70         else{
 71             nw[id[i]]+=ad[id[i]];
 72             t2[++cnt2]=id[i];
 73         }
 74     }
 75     for(int i=ql;i<=mid;i++){
 76         if(t[i].l<=t[i].r){
 77             add(t[i].l,-t[i].v);
 78             add(t[i].r+1,t[i].v);
 79         }else{
 80             add(1,-t[i].v);
 81             add(t[i].r+1,t[i].v);
 82             add(t[i].l,-t[i].v);
 83         }
 84     }
 85     for(int i=1;i<=cnt1;i++)id[l+i-1]=t1[i];
 86     for(int i=1;i<=cnt2;i++)id[l+cnt1+i-1]=t2[i];
 87     solve(ql,mid,l,l+cnt1-1);
 88     solve(mid+1,qr,l+cnt1,r);
 89 }
 90 int main(){
 91     memset(head,-1,sizeof(head));
 92     scanf("%d%d",&n,&m);
 93     for(int i=1;i<=m;i++){
 94         scanf("%d",&x);
 95         add(x,i);
 96     }
 97     for(int i=1;i<=n;i++){
 98         scanf("%lld",&num[i]);
 99         id[i]=i;
100     }
101     scanf("%d",&k);
102     for(int i=1;i<=k;i++){
103         scanf("%d%d%lld",&t[i].l,&t[i].r,&t[i].v);
104     }
105     t[++k]=task(1,m,inf);
106     solve(1,k,1,n);
107     for(int i=1;i<=n;i++){
108         if(ans[i]<k)printf("%d\n",ans[i]);
109         else puts("NIE");
110     }
111     return 0;
112 }

【BZOJ3110】K大数查询

其实这是个树套树题……(大雾)树状数组区间修改+区间查询很难搞所以写了个线段树

ps:双倍经验->【ZJOI2013】KSHKM的学习小组

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<cmath>
  6 #include<queue>
  7 #define inf 2147483647
  8 #define eps 1e-9
  9 using namespace std;
 10 typedef long long ll;
 11 typedef double db;
 12 struct node{
 13     int cl;
 14     ll v,laz;
 15 }t[500001];
 16 struct task{
 17     int l,r,id;
 18     ll v,ans;
 19 }q[50001],ad[50001],_q[50001];
 20 int n,m,op,nq=0,nad=0;
 21 bool cmp(task a,task b){
 22     return a.id<b.id;
 23 }
 24 bool cmp1(task a,task b){
 25     return a.v<b.v;
 26 }
 27 void pd(int u,int l,int r){
 28     int mid=(l+r)/2;
 29     if(t[u].cl){
 30         t[u*2].v=t[u*2].laz=t[u*2+1].v=t[u*2+1].laz=0;
 31         t[u*2].cl=t[u*2+1].cl=1;
 32         t[u].cl=0;
 33     }
 34     if(t[u].laz){
 35         t[u*2].laz+=t[u].laz;
 36         t[u*2].v+=(mid-l+1)*t[u].laz;
 37         t[u*2+1].laz+=t[u].laz;
 38         t[u*2+1].v+=(r-mid)*t[u].laz;
 39         t[u].laz=0;
 40     }
 41 }
 42 void updata(int l,int r,int u,int L,int R){
 43     pd(u,l,r);
 44     if(L<=l&&r<=R){
 45         t[u].v+=(r-l+1);
 46         t[u].laz++;
 47         return;
 48     }
 49     int mid=(l+r)/2;
 50     if(L<=mid)updata(l,mid,u*2,L,R);
 51     if(mid<R)updata(mid+1,r,u*2+1,L,R);
 52     t[u].v=t[u*2].v+t[u*2+1].v;
 53 }
 54 ll query(int l,int r,int u,int L,int R){
 55     if(L<=l&&r<=R){
 56         return t[u].v;
 57     }
 58     pd(u,l,r);
 59     int mid=(l+r)/2;
 60     ll ret=0;
 61     if(L<=mid)ret+=query(l,mid,u*2,L,R);
 62     if(mid<R)ret+=query(mid+1,r,u*2+1,L,R);
 63     return ret;
 64 }
 65 void solve(int ql,int qr,int l,int r){
 66     if(ql>qr||l>r)return;
 67     if(ql==qr){
 68         for(int i=l;i<=r;i++){
 69             q[i].ans=ad[ql].v;
 70         }
 71         return;
 72     }
 73     int mid=(ql+qr)/2,cnt1=mid+1,cnt2=l,L=l-1,R=r+1;
 74     t[1].cl=1;
 75     t[1].v=t[1].laz=0;
 76     sort(ad+ql,ad+qr+1,cmp1);
 77     sort(ad+mid+1,ad+qr+1,cmp);
 78     sort(q+l,q+r+1,cmp);
 79     while(cnt2<=r){
 80         if(cnt1<=qr&&ad[cnt1].id<q[cnt2].id){
 81             updata(1,n,1,ad[cnt1].l,ad[cnt1].r);
 82             cnt1++;
 83         }else{
 84             ll nw=query(1,n,1,q[cnt2].l,q[cnt2].r);
 85             if(nw>=q[cnt2].v)_q[--R]=q[cnt2];
 86             else{
 87                 q[cnt2].v-=nw;
 88                 _q[++L]=q[cnt2];
 89             }
 90             cnt2++;
 91         }
 92     }
 93     for(int i=l;i<=r;i++)q[i]=_q[i];
 94     solve(ql,mid,l,L);
 95     solve(mid+1,qr,R,r);
 96 }
 97 int main(){
 98     scanf("%d%d",&n,&m);
 99     for(int i=1;i<=m;i++){
100         scanf("%d",&op);
101         if(op==1){
102             nad++;
103             scanf("%d%d%lld",&ad[nad].l,&ad[nad].r,&ad[nad].v);
104             ad[nad].id=i;
105         }else{
106             nq++;
107             scanf("%d%d%lld",&q[nq].l,&q[nq].r,&q[nq].v);
108             q[nq].id=i;
109         }
110     }
111     solve(1,nad,1,nq);
112     sort(q+1,q+nq+1,cmp);
113     for(int i=1;i<=nq;i++){
114         printf("%lld\n",q[i].ans);
115     }
116     return 0;
117 }
posted @ 2018-12-10 13:53  DCDCBigBig  阅读(611)  评论(0编辑  收藏  举报