整体二分

分析

  • 对整体进行二分,自带一个\(lg\),通常需要用数据结构维护
  • 每次将答案的区间均分,询问,插入依次到应该在的地方,由于最多均分\(lg\)次,所以每个操作最多做\(lg\)

基操

区间第K大等等状态具有二分性

模板

ybtoj B. 【例题2】动态排名

将原数列看作插入,将修改看作删除+插入

用树状数组维护01,时间复杂度是\(O(nlg^2n)\)

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5,M=3e5+5;
int n,m,cnt,c[N],ans[N],a[N];
struct A{int fl,x,y,k,id; }q[M],q1[M],q2[M];
char s[100];
inline void add(int x,int k) {
	for(;x<=n;x+=x&-x) c[x]+=k;
}
inline int ask(int x) {
	int ret=0;
	for(;x;x-=x&-x) ret+=c[x];
	return ret;
}
void work(int ql,int qr,int L,int R) {
	if(ql>qr) return;
	if(L==R) {
		for(int i=ql;i<=qr;i++) {
			if(q[i].fl==1) ans[q[i].id]=L;
		}
		return;
	}
	int mid=L+R>>1,t1=0,t2=0;
	for(int i=ql;i<=qr;i++) {
		if(q[i].fl!=1) {
			if(q[i].y<=mid) {
				add(q[i].x,q[i].fl?-1:1);
				q1[++t1]=q[i];
			}  else q2[++t2]=q[i];
		} else {
			int t=ask(q[i].y)-ask(q[i].x-1);
			if(t>=q[i].k) q1[++t1]=q[i];
				else q[i].k-=t,q2[++t2]=q[i];
		}
	}
	for(int i=ql;i<=qr;i++) {
		if(q[i].fl!=1) {
			if(q[i].y<=mid) {
				add(q[i].x,q[i].fl?1:-1);
			}
		}
	}
	int k=ql;
	for(int i=1;i<=t1;i++,k++) q[k]=q1[i];
	for(int i=1;i<=t2;i++,k++) q[k]=q2[i];
	work(ql,ql+t1-1,L,mid),work(ql+t1,qr,mid+1,R);
}
int main() {
	scanf("%d%d",&n,&m); 
	for(int i=1;i<=n;i++) {
		q[i].x=i;
		scanf("%d",&q[i].y);
		a[i]=q[i].y;
	}
	cnt=n; int Q=0;
	for(int i=1;i<=m;i++) {
		scanf("%s",s+1);
		if(s[1]=='Q') {
			q[++cnt].fl=1; 
			scanf("%d%d%d",&q[cnt].x,&q[cnt].y,&q[cnt].k); 
			q[cnt].id=++Q;
		} else {
			int x,y; scanf("%d%d",&x,&y);
			q[++cnt].fl=2,q[cnt].x=x,q[cnt].y=a[x];
			q[++cnt].fl=0,q[cnt].x=x,q[cnt].y=y;
			a[x]=y;
		}
	}
	work(1,cnt,0,1e9);
	for(int i=1;i<=Q;i++) {
		printf("%d\n",ans[i]);
	}
	return 0;
}

例题1

Luogu P3332 [ZJOI2013]K大数查询

整体二分后区间修改,区间查询,有线段树

WA了一次:读入是没开longlong

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=5e4+5,M=4e5+5;
int n,m,ans[N];
ll c[M],t[M];
struct A{int fl,l,r,id; ll k; }q[N],q1[N],q2[N];

inline void down(int p,int l,int r,int mid) {
	if(t[p]) {
		t[p<<1]+=t[p],c[p<<1]+=(ll)t[p]*(mid-l+1);
		t[p<<1|1]+=t[p],c[p<<1|1]+=(ll)t[p]*(r-mid);
		t[p]=0;
	}
}
inline void up(int p) {
	c[p]=c[p<<1]+c[p<<1|1];
}
void add(int p,int l,int r,int x,int y,int k) {
	if(l==x&&r==y) {
		c[p]+=k*(r-l+1),t[p]+=k; return; 
	}
	int mid=l+r>>1;
	down(p,l,r,mid);
	if(y<=mid) add(p<<1,l,mid,x,y,k);
		else if(x>mid) add(p<<1|1,mid+1,r,x,y,k);
			else {
				add(p<<1,l,mid,x,mid,k);
				add(p<<1|1,mid+1,r,mid+1,y,k);
			}
	up(p);
}
ll ask(int p,int l,int r,int x,int y) {
	if(l==x&&r==y) return c[p];
	int mid=l+r>>1;
	down(p,l,r,mid);
	if(y<=mid) return ask(p<<1,l,mid,x,y);
	if(x>mid) return ask(p<<1|1,mid+1,r,x,y);
	return ask(p<<1,l,mid,x,mid)+ask(p<<1|1,mid+1,r,mid+1,y);
}
void work(int ql,int qr,int L,int R) {
	if(ql>qr) return;
	if(L==R) {
		for(int i=ql;i<=qr;i++) {
			if(q[i].fl==2) ans[q[i].id]=L;
		}
		return;
	}
	int mid=L+R>>1,t1=0,t2=0;
	for(int i=ql;i<=qr;i++) {
		if(q[i].fl==1) {
			if(q[i].k>mid) {
				add(1,1,n,q[i].l,q[i].r,1);
				q2[++t2]=q[i];
			}  else q1[++t1]=q[i];
		} else {
			ll t=ask(1,1,n,q[i].l,q[i].r);
			if(t>=q[i].k) q2[++t2]=q[i];
				else q[i].k-=t,q1[++t1]=q[i];
		}
	}
	for(int i=ql;i<=qr;i++) {
		if(q[i].fl==1) {
			if(q[i].k>mid) {
				add(1,1,n,q[i].l,q[i].r,-1);
			}
		}
	}
	int k=ql;
	for(int i=1;i<=t1;i++,k++) q[k]=q1[i];
	for(int i=1;i<=t2;i++,k++) q[k]=q2[i];
	work(ql,ql+t1-1,L,mid),work(ql+t1,qr,mid+1,R);
}
int main() {
	scanf("%d%d",&n,&m); int Q=0;
	for(int i=1;i<=m;i++) {
		scanf("%d%d%d%lld",&q[i].fl,&q[i].l,&q[i].r,&q[i].k);
		if(q[i].fl==2) q[i].id=++Q; 
	}
	work(1,m,-1e9,1e9);
	for(int i=1;i<=Q;i++) {
		printf("%d\n",ans[i]);
	}
	return 0;
}

例题3

Luogu P3527 [POI2011]MET-Meteors

显然是否收集完具有单调性,所以考虑整体二分

由于每个操作最多做\(lg\)次,所以直接暴力枚举某个时间前的空间站的情况即可

#include<bits/stdc++.h>
#define ll long long
const int INF=1e9;
using namespace std;

const int N=3e5+5,M=1e6+5;
int n,m,ans[N],b[N];
ll c[M];
vector<int>V[N];
struct A{int fl,l,r,id; ll k; }q[M],q1[M],q2[M];

inline void add(int x,int k) {
	for(;x<=m;x+=x&-x) c[x]+=k;
}
inline ll ask(int x) {
	ll ret=0;
	for(;x;x-=x&-x) ret+=c[x];
	return min(ret,(ll)INF);
}
void work(int ql,int qr,int L,int R) {
	if(ql>qr) return;
	if(L==R) {
		for(int i=ql;i<=qr;i++) {
			if(q[i].fl) {
				ans[q[i].id]=L;
			}
		}
		return;
	}
	int mid=L+R>>1,t1=0,t2=0;
	for(int i=ql;i<=qr;i++) {
		if(!q[i].fl) {
			if(q[i].id<=mid) {
				add(q[i].l,q[i].k);
				add(q[i].r+1,-q[i].k);
				q1[++t1]=q[i];
			}  else q2[++t2]=q[i];
		} else {
			ll t=0;
			for(int v:V[q[i].l]) {
				t+=ask(v);
			}
			if(t>=q[i].k) q1[++t1]=q[i];
				else q[i].k-=t,q2[++t2]=q[i];
		}
	}
	for(int i=ql;i<=qr;i++) {
		if(!q[i].fl&&q[i].id<=mid) {
			add(q[i].l,-q[i].k);
			add(q[i].r+1,q[i].k);
		}
	}
	int k=ql;
	for(int i=1;i<=t1;i++,k++) q[k]=q1[i];
	for(int i=1;i<=t2;i++,k++) q[k]=q2[i];
	work(ql,ql+t1-1,L,mid),work(ql+t1,qr,mid+1,R);
}
int main() {
	scanf("%d%d",&n,&m); int Q=0,cnt=0;
	for(int i=1;i<=m;i++) {
		int t; scanf("%d",&t);
		V[t].push_back(i);
	}
	for(int i=1;i<=n;i++) scanf("%d",&b[i]);
	scanf("%d",&Q);
	for(int i=1;i<=Q;i++) {
		int l,r,k; scanf("%d%d%d",&l,&r,&k);
		if(l<=r) {
			q[++cnt].l=l,q[cnt].r=r,q[cnt].k=k,q[cnt].id=i;
		} else {
			q[++cnt].l=l,q[cnt].r=m,q[cnt].k=k,q[cnt].id=i;
			q[++cnt].l=1,q[cnt].r=r,q[cnt].k=k,q[cnt].id=i;
		}
	}
	q[++cnt].l=1,q[cnt].r=m,q[cnt].k=INF,q[cnt].id=Q+1;
	for(int i=1;i<=n;i++) {
		q[++cnt].fl=1,q[cnt].l=i,q[cnt].k=b[i],q[cnt].id=i;
	}
	work(1,cnt,1,Q+1);
	for(int i=1;i<=n;i++) {
		if(ans[i]!=Q+1) {
			printf("%d\n",ans[i]);
		} else puts("NIE");
	}
	return 0;
}

例题4

Luogu P3242 [HNOI2015] 接水果

可以根据dfn判断子路径(分类讨论LCA)

然后变成区间覆盖,单点查询

考虑第K大,所以整体二分,变成平面修改,单点查询

由于保证了询问在修改前,所以可以离线采用扫描线+树状数组,可以事先按一维排序(一维相同,询问在修改后),在进行整体二分,时间复杂度$O(nlg^2n)

WA了一次:没考虑清lca重合的情况(红色表示选择范围,绿色表示链)

RE了一次:数组开小(LCA的)

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=4e4+5,M=2.4e5+5;
int n,m,q,id[N],sz[N],ans[N],c[N],b[M],fid[N];
struct A{int id,l,r,k,fh; }a[M],q1[M],q2[M];
vector<int>V[N];

namespace LCA {
	int f[N][16],lg[N],d[N],sgn;
	void dfs(int fa,int u) {
		id[u]=++sgn,sz[u]=1,d[u]=!fa?0:d[fa]+1;
		f[u][0]=fa;
		for(int i=1;i<=lg[d[u]];i++) {
			f[u][i]=f[f[u][i-1]][i-1];
		}
		for(int v:V[u]) {
			if(v!=fa) {
				dfs(u,v);
				sz[u]+=sz[v];
			}
		}
	}
	inline int son(int u,int v)  {
		for(int i=lg[d[v]-d[u]];i>=0;i--) {
			if(d[f[v][i]]>d[u]) v=f[v][i];
		}
		return v;
	}
	inline int lca(int u,int v) {
		if(d[u]>d[v]) swap(u,v);
		while(d[u]<d[v]) v=f[v][lg[d[v]-d[u]]];
		if(u==v) return u;
		for(int i=lg[d[u]];i>=0;i--) {
			if(f[u][i]!=f[v][i]) {
				u=f[u][i],v=f[v][i];
			}
		}
		return f[u][0];
	}
	void init() {
		lg[0]=-1;
		for(int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
		dfs(0,1);
	}
}

inline void add(int x,int k) {
	for(;x<=n;x+=x&-x) c[x]+=k; 
}
inline int ask(int x) {
	int ret=0;
	for(;x;x-=x&-x) ret+=c[x];
	return ret;
}
void work(int ql,int qr,int L,int R) {
	if(ql>qr) return;
	if(L==R) {
		for(int i=ql;i<=qr;i++) {
			if(!a[i].fh) {
				ans[a[i].l]=L;
			}
		}
		return;
	}
	int mid=L+R>>1,t1=0,t2=0;
	for(int i=ql;i<=qr;i++) {
		if(a[i].fh) {
			if(a[i].k<=mid) {
				q1[++t1]=a[i];
				add(a[i].l,a[i].fh),add(a[i].r+1,-a[i].fh);
			} else q2[++t2]=a[i];
		} else {
			int t=ask(a[i].r);
			if(a[i].k<=t) q1[++t1]=a[i];
				else a[i].k-=t,q2[++t2]=a[i];
		}
	}
	for(int i=1;i<=t1;i++) a[ql+i-1]=q1[i];
	for(int i=1;i<=t2;i++) a[ql+t1+i-1]=q2[i];
	work(ql,ql+t1-1,L,mid),work(ql+t1,qr,mid+1,R);
}
inline bool cmp(A i,A j) {
	return i.id<j.id||i.id==j.id&&abs(i.fh)>abs(j.fh);
}
inline bool cmp2(int i,int j) {
	return a[i].k<a[j].k;
}
inline int rd() {
	int ret=0; char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	for(;ch>='0'&&ch<='9';ch=getchar()) ret=(ret<<1)+(ret<<3)+ch-'0';
	return ret;
}
int main() {
	n=rd(),m=rd(),q=rd();
	for(int i=2;i<=n;i++) {
		int u=rd(),v=rd();
		V[u].push_back(v),V[v].push_back(u);
	}
	LCA::init(); int cnt=0;
	for(int i=1;i<=m;i++) {
		int u=rd(),v=rd(),k=rd();
		int l=LCA::lca(u,v);
		if(id[u]>id[v]) swap(u,v);
		if(u!=l) 
			a[++cnt]=(A){id[u],id[v],id[v]+sz[v]-1,k,1};
			a[++cnt]=(A){id[u]+sz[u],id[v],id[v]+sz[v]-1,k,-1};
		} else {
			u=LCA::son(u,v);
			a[++cnt]=(A){1,id[v],id[v]+sz[v]-1,k,1};
			a[++cnt]=(A){id[u],id[v],id[v]+sz[v]-1,k,-1};
			if(id[u]+sz[u]<=n) {
				a[++cnt]=(A){id[v],id[u]+sz[u],n,k,1};
				a[++cnt]=(A){id[v]+sz[v],id[u]+sz[u],n,k,-1};
			}
		}
	}
	for(int i=1;i<=cnt;i++) b[i]=i;
	sort(b+1,b+cnt+1,cmp2);
	int num=0; fid[num]=a[b[1]].k,a[b[1]].k=num;
	for(int i=2;i<=cnt;i++) {
		if(a[b[i]].k!=fid[a[b[i-1]].k]) num++,fid[num]=a[b[i]].k;
		a[b[i]].k=num;
	}
	for(int i=1;i<=q;i++) {
		int u=rd(),v=rd(),k=rd();
		if(id[u]>id[v]) swap(u,v);
		a[++cnt]=(A){id[u],i,id[v],k,0};
	}
	sort(a+1,a+cnt+1,cmp); 
	work(1,cnt,0,num);
	for(int i=1;i<=q;i++) {
		printf("%d\n",fid[ans[i]]);
	}
	return  0;
}
posted @ 2021-04-05 20:41  wwwsfff  阅读(38)  评论(0编辑  收藏  举报