【LOJ2392】「JOISC 2017 Day 1」烟花棒

【题目链接】

【思路要点】

  • 可以发现,让一个人 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值