LG-P3550 [POI2013]TAK-Taxis 题解
LG-P3550 [POI2013]TAK-Taxis Solution
更好的阅读体验戳此进入
题面
存在一条线段,有三个位置,$ 0 \((即原点)为 Byteasar 初始所在的位置,\) d $ 为出租车总部位置,$ m $ 为目的地位置,有 $ n $ 辆出租车,每辆出租车都有一个行驶路程上限 $ x_i $,每辆出租车都从总部出发,并且最终可以不回到总部。Byteasar 能搭乘出租车行动,但是他可以在任意时刻任意点改变出租车。
求最少需要多少辆出租车才能使 Byteasar 抵达目的地,无解输出 $ 0 $。
$ 1 \le d \le m \le 10^{18}, 1 \le n \le 5 \times 10^5, 1 \le x_i \le 10^{18} $。
输入格式
$ m $ $ d $ $ n $
$ x_1, x_2, \cdots, x_n $
Solution
(建议您从上方链接进入我的个人网站查看此 Blog,在 Luogu 中图片会被墙掉,部分 Markdown 也会失效)
对于本题的思路和代码实现题解区的大佬们已经说的很详细了,这里主要对该题贪心策略中的一些细节进行论述与证明。
本来看这题面感觉很像一道 DP,然后口糊了半天也没想到什么优秀的状态和转移。。。
所以这道题是个贪心。
大概就是显然每辆车是需要先先走到人所在的位置然后向着总部走,这个过程我们可以考虑到显然优先选路程上限更大的车是更优的,这个很显然吧,如果选路程小的那么去掉前往人所在位置的路程,最终的贡献就会更小,会导致后面的车无用的路程更多,贡献也会变小。
但如果只考虑这样可能会导致因为前面为了解更优而耗费了过多的大车导致最终只剩下路程小于 $ m - d $ 的小车,从而使有解变为无解,所以我们需要为最后的 $ m - d $ 的路程预留一个路程最短但 $ x_i \ge m - d $ 的车,然后按照刚才的思路贪心。
然后贪心是正确的,但我的之前证明假了。。
update:
考虑对于最后一段 $ m - d $ 的路程一定是需要通过一辆至少为 $ m - d $ 的车来送走的,于是我们预留下车之后,设预留的车为 $ \xi $,且 $ \xi \ge m - d $,剩下的车我们按照从大到小贪心地使用,那么如果最后一辆车可以在到达 $ d $ 以前直接送到 $ m $,那么显然设这辆车为 $ \epsilon $,设车 $ \epsilon $ 需要向 $ 0 $ 走的路程为 $ \alpha $,那么一定满足 $ \epsilon \ge \alpha \times 2 + m - d $ 且 $ \xi \le \epsilon $,这样显然我们完全不需要用预留的 $ \xi $ 这辆车。
如果最后一辆车不能送到终点,那么在有解的情况下一定至少都再需要一辆大于 $ m - d $ 的车,那么按照我们的贪心思路选择最小的大于 $ m - d $ 的车一定是最优的,或者说不劣的,$ \square $。
下面的内容均为之前写的假的证明,思路错了,这里留作纪念。
这时候仔细想一下就会发现一个问题,如果我们在到达总部之前的,乘坐的最后一辆车的行驶距离足够长,可以把我们送到超过总部 $ \xi $ 距离的位置,那么这个时候我们就只需要 $ x_i \ge m - d - \xi $ 的车就可以达到目标,那么有没有可能因为我们最初预留的那辆车距离过长而导致答案更劣?
我们可以考虑令按照贪心策略预留的车距离为 $ \alpha $,令在到达总部之前选择的所有车距离的最小值为 $ \beta $,令走到总部前倒数第二辆车到达的位置距离总部的位置为 $ \epsilon $。
显然如果 $ \beta \ge 2 \times \epsilon + (m - d) $,那么到达总部前的最后一辆车就已经可以带我们走到目的地,那么预留的车便可以无需使用,答案减少 $ 1 $。
反之则为一般情况:
若 $ \beta \ge \alpha $:那么显然需要使用预留的车。
若 $ \beta \lt \alpha $:此时我们可能会认为将 $ \alpha, \beta $ 互换,或者说将 $ \alpha $ 变为一个更小一点的车,会令结果更优,但是仔细思考一下就会发现,因为此时显然因为 $ \beta \lt 2 \times \epsilon + (m - d) $,所以即使改变 $ \alpha $,也只能使到达总部前的最后一辆车到达超过总部比 $ \xi $ 更远的位置,依然不能直接到达 $ m $,所以此时是改变 $ \alpha $,只会使方案改变,而不会使最少使用的车辆数改变,最终仍然需要额外的一辆车来送到最终的目的地。
同时注意,上述我们论述的均为到达总部前最后一辆车能将我们送到超过总部的位置,如果最后一辆车无法将我们送达总部,我们也不能直接判定为无解,需要考虑到预留的那辆车也可以先向总部左侧行驶一段后再向右行驶到目的地,所以在跳出循环之后还需要判断一下。
因此在程序中我们除了判定是否满足到达 $ m $ 之外,也需要判断是否到达了可以直接使用预留的车辆送达目的地的位置,如果到了就直接输出 $ ans + 1 $ 即可。
Code
#define _USE_MATH_DEFINES
#include <bits/extc++.h>
#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}
/******************************
abbr
******************************/
using namespace std;
using namespace __gnu_pbds;
mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}
typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;
#define int ll
template<typename T = int>
inline T read(void);
int M, D, N;
vector < int > dis;
signed main(){
// freopen("in.txt", "r", stdin);
M = read(), D = read(), N = read();
for(int i = 1; i <= N; ++i)dis.push_back(read());
sort(dis.begin(), dis.end(), greater < int >());
auto ptr = lower_bound(dis.rbegin(), dis.rend(), M - D);
if(ptr == dis.rend()){printf("0\n"); return 0;}
int val = *ptr;
dis.erase((++ptr).base());
int cur(0), ans(0);
for(auto i : dis){
cur += i - (D - cur);
++ans;
if(cur <= 0){--ans; break;}
if(cur >= M){printf("%lld\n", ans); return 0;}
if(cur >= D - (val - (M - D)) / 2){printf("%lld\n", ans + 1); return 0;}
// printf("cur:%d, ans:%d\n", cur, ans);
}
if((D - cur) * 2 + (M - D) <= val)printf("%lld\n", ans + 1);
else printf("0\n");
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template<typename T>
inline T read(void){
T ret(0);
short flag(1);
char c = getchar();
while(c != '-' && !isdigit(c))c = getchar();
if(c == '-')flag = -1, c = getchar();
while(isdigit(c)){
ret *= 10;
ret += int(c - '0');
c = getchar();
}
ret *= flag;
return ret;
}
UPD
update-2022_09_XX 初稿
update-2022_10_08 修改了错误的贪心证明