CF875EDelivery Club 二分+线段树

Posted CQzhangyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF875EDelivery Club 二分+线段树相关的知识,希望对你有一定的参考价值。

【CF875E】Delivery Club

题意:有n个快递需要依次接收,这n个快递分部在x轴上,第i个快递的位置是xi。有两个快递员,一开始分别在s0,s1,你可以任意安排哪个人收哪个快递,前提是一个快递员收快递是另一个快递员不能移动(也就是说他只有在收快递时能移动),并且要保证任何时候两人的距离不超过k。问你k最小是多少。

n<=10^5,xi<=10^9

题解:二分是显然的。我们可以用f[i][a][b]表示收第i个快递时,两个快递员一个在a,一个在b是否可行,又因为a或b一定等于i,所以我们可以省掉一维。我们还可以用线段树再省一维。因为在收第i+1个快递时,要么是在i处的快递员走到i+1,此时与i+1距离超过k的位置a都变成了不合法的,可以用线段树区间清零搞定;要么是在a处的快递员走到i+1。用线段树很容易维护这些东西。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int maxn=100010;
int n;
int v[maxn],p[maxn],rnk[maxn];
bool s[maxn<<2];
inline int Abs(const int &a) {return a>0?a:-a;}
bool cmp(const int &a,const int &b)
{
	return v[a]<v[b];
}
inline void pushdown(int x)
{
	if(!s[x])	s[lson]=s[rson]=0;
}
void modify(int l,int r,int x,int a)
{
	if(l==r)
	{
		s[x]=1;
		return ;
	}
	pushdown(x);
	int mid=(l+r)>>1;
	if(a<=mid)	modify(l,mid,lson,a);
	else	modify(mid+1,r,rson,a);
	s[x]=s[lson]|s[rson];
}
void updata(int l,int r,int x,int a,int b)
{
	if(a>b)	return ;
	if(a<=l&&r<=b)
	{
		s[x]=0;
		return ;
	}
	pushdown(x);
	int mid=(l+r)>>1;
	if(a<=mid)	updata(l,mid,lson,a,b);
	if(b>mid)	updata(mid+1,r,rson,a,b);
	s[x]=s[lson]|s[rson];
}
bool check(int len)
{
	int i,l,r,mid;
	s[1]=0;
	modify(1,n,1,rnk[1]);
	for(i=3;i<=n;i++)
	{
		l=1,r=rnk[i];
		while(l<r)
		{
			mid=(l+r)>>1;
			if(v[p[mid]]<v[i]-len)	l=mid+1;
			else	r=mid;
		}
		updata(1,n,1,1,l-1);
		l=rnk[i],r=n;
		while(l<r)
		{
			mid=(l+r)>>1;
			if(v[p[mid]]<=v[i]+len)	l=mid+1;
			else	r=mid;
		}
		updata(1,n,1,l,n);
		if(Abs(v[i]-v[i-1])<=len)	modify(1,n,1,rnk[i-1]);
		if(!s[1])	return 0;
	}
	return 1;
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
int main()
{
	n=rd()+2;
	int i,l=0,r=0,mid;
	for(i=1;i<=n;i++)	v[i]=rd(),r=max(r,v[i]),p[i]=i;
	sort(p+1,p+n+1,cmp);
	for(i=1;i<=n;i++)	rnk[p[i]]=i;
	v[0]=-1000000000,v[n+1]=1000000000;
	l=Abs(v[2]-v[1]);
	while(l<r)
	{
		mid=(l+r)>>1;
		if(check(mid))	r=mid;
		else	l=mid+1;
	}
	printf("%d",r);
	return 0;
}

以上是关于CF875EDelivery Club 二分+线段树的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces 875E Delivery Club dp

CF887ELittle Brother 二分+几何

CF1083C Max Mex(线段树上二分)

CF817F MEX Queries(线段树上二分)

「CF484E」Sign on Fence「整体二分」「线段树」

CF538H Summer Dichotomy 二分图扫描线线段树