【题目链接】
【思路要点】
- 可以发现,让一个人 A A A 点燃另一个人 B B B 后跑开的解一定能被让 A A A 跟着 B B B 直到 A A A 燃烧殆尽的解完全替代,因此我们可以假设任意时刻,局面中一定只有一个正在燃烧的人。
- 不在燃烧的人一定始终会向正在燃烧的人靠近,一旦到达正在燃烧的人,我们可以看做其燃烧时间增加了 t t t 。正在燃烧的人只有在遇到某人的时候才会转向。
- 二分答案,我们可以将过程转化为如下问题:
有两排人,必须分别按顺序点燃,点燃某人需要消耗一定燃烧时间 c o s t cost cost ,消耗的量不能超过当前的燃烧时间,此后可以获取一定燃烧时间 v a l u e value value ,问是否能将所有人全部点燃。- 考虑将每一排人分为极小的若干组,使得每一组全部点燃后燃烧时间会增加,且其任意一个前缀点燃后燃烧时间不会增加,我们用二元组 ( C o s t , I n c ) (Cost,Inc) (Cost,Inc) 来描述这样的一组,其中 C o s t Cost Cost 表示点燃该组至少需要的燃烧时间, I n c Inc Inc 表示点燃后将获得的燃烧时间。
- 若两排人均能完全分为这样的若干组,那么我们的策略就是点燃任意可以点燃的一组,若某一时刻不能点燃任意一组,则答案为否,否则为是。
- 若两排人不能完全分为这样的若干组,则意味着存在两排人存在一个后缀,使得其 c o s t cost cost 的总量大于 v a l u e value value 的总量。
- 考虑将过程反转,最终状态中我们手中的燃烧时间是确定的,可以把问题等价地看做:点燃某人需要消耗一定燃烧时间 v a l u e value value ,消耗的量不能超过当前的燃烧时间,此后可以获取一定燃烧时间 c o s t cost cost ,问是否能将所有人全部点燃,用同样的方式判断这两个后缀能否被点燃即可。
- 时间复杂度 O ( N L o g N ) O(NLogN) O(NLogN) 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 5; const long long INF = 1e18; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, k, t, a[MAXN]; bool check(ll s, deque <pair <ll, ll>> forw, deque <pair <ll, ll>> bakw) { ll now = s; while (!forw.empty() || !bakw.empty()) { if (!forw.empty() && forw.front().second >= 0 && forw.front().first <= now) { now += forw.front().second; forw.pop_front(); } else if (!bakw.empty() && bakw.front().second >= 0 && bakw.front().first <= now) { now += bakw.front().second; bakw.pop_front(); } else if (!forw.empty() && forw.front().first <= now) { now += forw.front().second; forw.pop_front(); } else if (!bakw.empty() && bakw.front().first <= now) { now += bakw.front().second; bakw.pop_front(); } else return false; } return true; } bool check(int v) { ll s = 1ll * v * t, dest = s; deque <pair <ll, ll>> forw, bakw; ll cost = 0, Max = 0, inc = 0; for (int i = k + 1; i <= n; i++) { cost += (a[i] - a[i - 1]) / 2; dest -= (a[i] - a[i - 1]) / 2; dest += s; chkmax(Max, cost - inc), inc += s; if (inc >= cost) { forw.emplace_back(Max, inc - cost); cost = 0, Max = 0, inc = 0; } } cost = 0, Max = 0, inc = 0; for (int i = k - 1; i >= 1; i--) { cost += (a[i + 1] - a[i]) / 2; dest -= (a[i + 1] - a[i]) / 2; dest += s; chkmax(Max, cost - inc), inc += s; if (inc >= cost) { bakw.emplace_back(Max, inc - cost); cost = 0, Max = 0, inc = 0; } } if (!check(s, forw, bakw)) return false; forw.clear(), bakw.clear(); cost = 0, Max = 0, inc = 0; for (int i = n; i >= k + 1; i--) { cost += s; chkmax(Max, cost - inc), inc += (a[i] - a[i - 1]) / 2; if (inc >= cost) { forw.emplace_back(Max, inc - cost); cost = 0, Max = 0, inc = 0; } } cost = 0, Max = 0, inc = 0; for (int i = 1; i <= k - 1; i++) { cost += s; chkmax(Max, cost - inc), inc += (a[i + 1] - a[i]) / 2; if (inc >= cost) { bakw.emplace_back(Max, inc - cost); cost = 0, Max = 0, inc = 0; } } if (!check(dest, forw, bakw)) return false; return true; } int main() { read(n), read(k), read(t), t *= 2; for (int i = 1; i <= n; i++) read(a[i]), a[i] *= 2; int l = 0, r = 1e9; while (l < r) { int mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid + 1; } writeln(l); return 0; }