Codeforces 875E Delivery Club 妙哉!

来源:互联网 发布:在华生活的日本人 知乎 编辑:程序博客网 时间:2024/06/05 04:45

题目链接:传送门

题目大意:给定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] - xR <- a[n] + xfor i <- (n-1) to 1 doif In_Range(a[i], L, R)L <- a[i] - xR <- a[i] + xelseL <- max(L, a[i] - x)R <- min(R, a[i] + x)End Forreturn  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 1000000000using 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;}


原创粉丝点击