浅谈整体二分

思路有些像CDQ,都是离线

整体二分满足以下性质: 
1. 询问的答案具有可二分性 
2. 修改对判定答案的贡献相对独立,修改之间互不影响效果 
3. 修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值 
4. 贡献满足交换律,结合律,具有可加性 
5. 题目允许离线操作

时间复杂度方面还要满足整体二分内部不能与序列的总长度相关,而要与当前的序列的长度相关

————————————————————————————————————————————————————————

例1:静态区间第K小

二分了答案,标记0/1后,(0表示小于等于mid,1表示大于mid),前缀和一下,然后再判断分解,继续递归

但是前缀和不能与序列总长度有关

然而可以发现一个性质:原来比之前[l,r]中的mid大的,肯定比[l,mid]中的mid大,所以只用在原来的基础上修改没一个数就好了

所以果断上个树状数组

#include<cstdio>
#include<algorithm>
using namespace std;

const int N=1e6+5;
int n,m,ql[N],qr[N],qk[N],id[N],p,n1,n2,c[N],ans[N],t1[N],t2[N];
struct A{int id,x; }a[N];
bool cmp(A i,A j){ return i.x<j.x; }

inline void add(int x,int val)
{
	for(int i=x;i<=n;i+=i&-i) c[i]+=val;
}

inline int sum(int x)
{
	int ret=0; 
	for(int i=x;i;i-=i&-i) ret+=c[i];
	return ret;
}

void work(int l,int r,int ll,int rr)
{
	if(l>r) return;
	int mid=ll+rr>>1;
	while(p+1<=n&&a[p+1].x<=mid) p++,add(a[p].id,1);
	while(p&&a[p].x>mid) add(a[p].id,-1),p--;
	n1=n2=l;
	for(int i=l;i<=r;i++)
		if(sum(qr[id[i]])-sum(ql[id[i]]-1)>=qk[id[i]]) 
			ans[id[i]]=mid,t1[n1++]=id[i];
		else t2[n2++]=id[i];
	for(int i=l;i<n1;i++) id[i]=t1[i];
	for(int i=n1;i<=r;i++) id[i]=t2[i-n1+l];
	if(ll==rr) return;
	work(l,n1-1,ll,mid),work(n1,r,mid+1,rr);
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) 
		scanf("%d",&a[i].x),a[i].id=i;
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=m;i++) 
		scanf("%d%d%d",&ql[i],&qr[i],&qk[i]),id[i]=i;
	work(1,n,a[1].x,a[n].x);
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
	return 0;
 } 

例2:动态区间第K小

扫描二维码关注公众号,回复: 4329487 查看本文章

还没想清楚,留个坑

猜你喜欢

转载自blog.csdn.net/YYHS_WSF/article/details/84703152
今日推荐