整体二分
鬼知道为什么这一个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 }