整体二分

整体二分

今天刚透彻了整体二分,写篇blog;
由于整体二分比较难以口述,来道例题讲解
静态区间第k小
同时也是主席树模板题,可是主席树模板怎么能用主席树写呢
往下看,不会主席树的也可以水过这道紫体题了;
首先设A序列表示表示给定序列;
所有询问的答案一定在MINA~MAXA之间;
对MINA~MAXA进行二分答案;
二分答案一个询问想必大家都会,单数这道题时间不允许;
考虑对整体进行二分,对于所有询问二分;
也就是对询问进行分类,考虑二分出的答案mid;
用树状数组维护小于等于mid的个数;
对于每个区间ask(r)-ask(l-1)可以知道在这个询问区间里小于等于mid的个数;
就可以把所有询问分为两类,设num为l~r里小于等于mid的个数;
第一类:num<=k 第二类num>k;
考虑第一类询问的答案一定在L~mid之间;
第二类询问的答案一定在mid+1~R之间;
L,R表示当前的答案区间;
那么就可以把询问分成两类在分别将行分治
知道L==R为止,L就是答案;
注意A序列也要分开;
考虑对于第一类询问,A序列中>mid的值对这些区间没有影响
所以可以直接舍弃;
对于第二类询问,它们的答案一定在mid+1~r之间
所以只考虑>mid的值;
例题

#include<iostream>
#include<cstdio>
using namespace std;
const int N=2e5+7;
const int inf=1e9; 
struct node{
	int op,x,y,z;
}q[N<<1],ql[N<<1],qr[N<<1];
int n,m,cnt;
int ans[N],t[N];
int lowbit(int x){
	return x&-x;
}
void change(int x,int val){
	for(;x<=N-7;x+=lowbit(x)){
		t[x]+=val;
	}
}
int ask(int x){
	int res=0;
	for(;x;x-=lowbit(x)){
		res+=t[x];
	}
	return res;
}
void work(int L,int R,int l,int r){
	if(l>r) return;
	if(L==R){
		for(int i=l;i<=r;i++){
			if(q[i].op>0) ans[q[i].op]=L;
		}
		return;
	}
	int mid=(L+R)>>1;
	int tl=0,tr=0;
	for(int i=l;i<=r;i++){
		if(q[i].op==0){//属于A序列
			if(q[i].y<=mid) change(q[i].x,1),ql[++tl]=q[i];
			else qr[++tr]=q[i];
		} else {//属于询问区间
			int tt=ask(q[i].y)-ask(q[i].x-1);
			if(tt>=q[i].z) ql[++tl]=q[i];
			else {
				q[i].z-=tt;//减去tt;
				qr[++tr]=q[i];
			}
 		}
	}
	for(int i=r;i>=l;i--){
		if(q[i].op==0&&q[i].y<=mid) change(q[i].x,-1);//清空树状数组;
	}
	for(int i=1;i<=tl;i++) q[l+i-1]=ql[i];
	for(int i=1;i<=tr;i++) q[l+tl+i-1]=qr[i];
	work(L,mid,l,l+tl-1);
	work(mid+1,R,l+tl,r);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&q[++cnt].y);
		q[cnt].op=0;q[cnt].x=i;//op分类
	}
	for(int i=1;i<=m;i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		q[++cnt].x=x;
		q[cnt].y=y;
		q[cnt].z=z;
		q[cnt].op=i;
	}
	work(-inf,inf,1,cnt);
	for(int i=1;i<=m;i++) cout<<ans[i]<<"\n";
}
posted @ 2020-09-06 19:48  Aswert  阅读(123)  评论(2编辑  收藏  举报