Codeforces 875E Delivery Club 妙哉!

题目链接:传送门

题目大意:给定n, s1, s2和数轴上n个点{a_n},编号1~n,要求给一开始分别在s1和s2的两个人分配任务,使得每个点按照 编号顺序 被恰好一个人访问,并且使得任意时刻两个人的距离的最大值最小。n<=1e5,s1, s2, ai<=1e9


题解:显然这个东西摆明了是要二分答案,然后,不难发现二分出来的答案的可行性可以用dp验证,大概就是dp[i]表示第i个点被访问是否(在这个二分答案下)可行。

这个转移显然是一段区间的或,可以用线段树维护一下,复杂度O(n(lgn)^2),可以通过,但是代码复杂度和时间复杂度都略高。

一个很显然的想法就是贪心,直观的贪心就是正着考虑,然后各种讨论,但是大概没有什么好的思路。

现在我们用一个转化思路的技巧:我们不求从s1,s2能不能完成任务,我们求,s1,s2在什么地方能完成任务;

这样球出来之后只有判断一下s1和s2在不在范围内即可。

显然在这里s1和s2无本质区别,下文用“当前这个人”和"另一个人"来分别表示一个人和另一个人。

这显然是一个倒序求解的问题,因为只做第n个任务,当前这个人的区间是显然的,就是[a[n]-二分出来的答案, a[n]+二分出来的答案];

而我们求得就是做1~n个任务的区间。算法流程如下(x为二分出来的答案):

L <- a[n] - x
R <- a[n] + x
for i <- (n-1) to 1 do
	if In_Range(a[i], L, R)
		L <- a[i] - x
		R <- a[i] + x
	else
		L <- max(L, a[i] - x)
		R <- min(R, a[i] + x)
End For

return  In_Range(s1, L, R) or In_Range(s2, L, R)

这段代码的大意就是,设置初始区间[L, R]为[a[n]-x, a[n]+x],然后i从n-1逆序枚举;

如果a[i]在当前区间[L,R]里,就把[L,R]设为以a[i]为中心x为半径的区间(下文记作a[i]的x-y区间)

否则[L, R]取为它和a[i]的x-y区间的交集。下文把[L, R]叫做当前区间,也就是当前这个人应该在的区间。

问什么是对的呢?读者可以尽量思考一下!


首先我们考虑a[i]在当前区间内的情况;假设当前这个人在编号为j的点上,显然有j>i;

那么我们证明在编号为i的人一定  可以  是另一个人。

假设不可以,即i也是当前这个人呆过的地方,那么假设当当前这个人在i的时候,另一个人在k;

并且不妨假设当前的人从i跳到j的时候,另一个人一直呆在k(否则你可以选择最后没有变化的那一段,情况不会改变)

显然有a[i]应当在a[k]的x-y区间内,而i最终直接跳到了j(如果不直接的话,那么假设i先到了t,那么t应该取代i的位置),

这意味着a[j]也是在a[k]的x-y区间内的,否则因为另一个人没动所以就到不了j了。

那么如果存在这种情况,我们不难发现,a[p]在a[q]的x-y区间内等价于a[q]在a[p]的x-y区间内,

因此上文等价于a[k]和a[j]在a[i]的x-y区间内,因此另一个人可以直接从a[k]跳到a[j],也就是a[k]和a[j]可以看作是同一人,这种情况下a[i]就是另一个人

上文的证明思路就是,如果i可以是当前这个人并且有解,那么把i换成另一个人也可以保证有解。

因此,把“当前这个人”和"另一个人"互换,即把另一个人看成当前这个人,那么当前区间显然就是[a[i]-x, a[i]+x]。


下面在看else部分,即a[i]不在当前区间内的情况,那么a[i]一定不会是当前这个人。这是定义,请参照上文。

因此,a[i]就是另一个人,这意味着,当前这个人也一定要在a[i]的x-y区间内,因此两个区间取交即可。

这样最后两个人只要有一个人在算出来的区间内(即他是当前这个人),二分出来的x就是ok的。

代码实现注意一开始二分的下界应该设成abs(s1-s2),否则会gg。


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inrange(x) ((x)>=L&&(x)<=R)
#define N 100010
#define INF 1000000000
using namespace std;
int a[N],n,s1,s2;
inline bool check(int x)
{
	int L=a[n]-x,R=a[n]+x;
	for(int i=n-1;i;i--)
		if(inrange(a[i])) L=a[i]-x,R=a[i]+x;
		else L=max(L,a[i]-x),R=min(R,a[i]+x);
	return inrange(s1)||inrange(s2);
}
inline int gabs(int x)
{
	return x>0?x:-x;
}
int main()
{
	scanf("%d%d%d",&n,&s1,&s2);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int L=gabs(s1-s2),R=INF,mid=(L+R)>>1;
	while(L<=R)
	{
		if(check(mid)) R=mid-1;
		else L=mid+1;mid=(L+R)>>1;
	}
	printf("%d\n",L);return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值