大佬 大佬在 n n n 天内第 i i i 天对你造成 a i a_i ai 伤害。你的初始血量 m c mc mc,等级 0,讽刺能力 1,每天可以选择以下操作之一:对大佬造成 1 伤害;回 w i w_i wi 血但不能超过 m c mc mc;使等级加 1;使讽刺能力乘等级;怼大佬,即对大佬造成等于讽刺能力的伤害,并使等级和讽刺能力恢复至初始——最后一种操作至多两次。若在 n n n 天内你血量非正数则你死亡。若在 n n n 天内大佬血量恰为 0 则大佬死亡,大佬的血量不能为负数。给出 m m m 个大佬初始血量,分别输出能否战胜之。
贪心。 本题难在实现,0xis 做到一半,本题在洛谷上从紫题变成黑题。普及考也说得过去,不过确实好题。
由于自己血量不影响伤害,可以先 DP 算出能造成伤害的最大天数 d r dr dr,并带哈希 BFS 得到所有「蓄力天数 d a y day day—伤害 f f f」数对 。如果不怼大佬可以战胜则 h p ≤ d r hp \le dr hp≤dr。怼一次则 f ≤ h p f \le hp f≤hp 且 h p − f ≤ d r − d a y hp - f \le dr - day hp−f≤dr−day,怼两次则 f 1 + f 2 ≤ h p f_1 + f_2 \le hp f1+f2≤hp 且 h p − f 1 − f 2 ≤ d r − d a y 1 − d a y 2 hp - f_1 - f_2 \le dr - day_1 - day_2 hp−f1−f2≤dr−day1−day2。
将数对排序可使决策单调。不怼可直接判断,怼一次可枚举。对于怼两次,枚举第二次怼,在 f 1 + f 2 ≤ h p f_1 + f_2 \le hp f1+f2≤hp 情况下扫第一次怼,使 d a y 1 − f 1 day_1 - f_1 day1−f1 最小判断即可。
#include <cstdio>
#include <algorithm>
#include <queue>
#define F(z, u, v) for(int z = (u), dest##z = (v); z <= dest##z; z++)
const int MAXN = 101, MAXT = 10000001, YIYI = 100000000;
typedef long long int LINT;
int n, m, mc, c, a[MAXN], w[MAXN], dp[MAXN][MAXN], dr, tc = 0;
#define Gt(u, v) ((u) = std::max((u), (v)))
#define Lt(u, v) ((u) = std::min((u), (v)))
struct TUP { int day, f, l; TUP() {}
TUP(int day, int f, int l): day(day), f(f), l(l) {}} tup[MAXT];
std::queue<TUP> q;
namespace Hash {
const int MAXH = 100000, MAXS = 10000001;
int cnt = 0, hd[1 + MAXH], f[MAXS], l[MAXS], dd[MAXS], po[MAXS];
int Code(int day, int u, int v) {
return 1 + ((LINT(u ^ 19260817) * day) ^ 20180801) % MAXH; }
void Add(int day, int u, int v) {
int k = Code(day, u, v); po[++cnt] = hd[k]; hd[k] = cnt;
f[cnt] = u; dd[cnt] = day; }
bool At (int day, int u, int v) {
for(int i = hd[Code(day, u, v)]; i; i = po[i])
if(day == dd[i] && u == f[i]) return true;
return false; }}
int main() {
scanf("%d%d%d", &n, &m, &mc);
F(i, 1, n) scanf("%d", a + i); F(i, 1, n) scanf("%d", w + i);
F(i, 1, n) F(j, a[i], mc)
Gt(dr, std::max(Gt(dp[i][j - a[i]], dp[i - 1][j] + 1),
Gt(dp[i][std::min(j + w[i] - a[i], mc)], dp[i - 1][j])));
q.push(TUP(1, 1, 0)); while(!q.empty()) {
int day = q.front().day, f = q.front().f, l = q.front().l; q.pop();
if(day >= dr) continue; q.push(TUP(day + 1, f, l + 1));
if(l > 1 && LINT(f) * l <= YIYI && !Hash::At(day + 1, f * l, l))
Hash::Add(day + 1, f * l, l),
q.push(tup[++tc] = TUP(day + 1, f * l, l)); }
std::sort(tup + 1, tup + tc + 1, [](TUP u, TUP v) {
return u.f != v.f? u.f < v.f: u.day < v.day; });
while(m--) {
int hp, lag = 0x70000000; bool f_ = true; scanf("%d", &hp);
if(hp <= dr) { puts("1"); continue; }
for(int l = 1, r = tc; r; r--) {
while(l < tc && tup[l].f + tup[r].f <= hp)
Lt(lag, tup[l].day - tup[l].f), l++;
if((lag + tup[r].day - tup[r].f + hp <= dr) ||
(tup[r].f <= hp && hp - tup[r].f + tup[r].day <= dr)) {
f_ = false; puts("1"); break; }}
if(f_) puts("0"); }}