HNOI 2017 怼大佬
题意
怼\(m\)个大佬,第\(i\)个大佬的自信值为\(C_i\)。每次怼大佬之前,你的自信值为\(mc\),等级\(L=0\),嘲讽值 \(F=1\) 。对于每一个大佬,都有\(n\)天时间,第\(i\)天你将会被大佬打击,自信值下降\(a_i\)。如果在某一天你的自信值为负数,那么你失败了
每一天你可以进行以下操作的任意一个:
-
还一句嘴,使大佬自信值下降\(1\)
-
刷水题,使自己的自信值增加\(w_i\)
-
把自己的等级\(+1\)
-
把自己的\(F\)乘上\(L\)
-
怼大佬,使得大佬的自信值下降 \(F\),之后 \(L = 0,F = 1\)。这个操作可以进行 \(k=\{0,1,2\}\) 次
你必须在某一天使大佬的自信值恰好为 \(0\) 。如果大佬的自信值为负,则判定你失败
对于每个大佬,请判断你是否成功
解法
可以发现胜利的必要条件是自己能在大佬被怼死之前存活下来
我们设一个DP \(f[i][j]\) 为在第 \(i\) 天,剩余自信值为 \(j\) 时最多能够把多少天用来怼大佬
在该数组中取一个最大值即可求出最多能有多少天用于怼大佬,把这个天数设置为 \(D\)
可以发现怼大佬这一操作的威力只与操作 \(4,5\) 有关
一遍 BFS 求出所有的二元组 \(<D, F>\) 代表花 \(D\) 天可以对大佬造成的伤害 \(F\)
贪心的思考,对于某一个 \(F\) ,我们只需要保留最小的 \(D\) 即可,这样可以减少很多状态
你能怼死大佬有三种情况:
-
不怼大佬,只还嘴。满足 \(C_i \leq D\) 即可
-
只怼一次大佬。这时能怼死大佬需要满足存在一个二元组 \((d',f')\)使得 \(f'\leq C_i\) 并且 \(f'+D-d' \ge C_i\)。(即怼不死的部分用还嘴耗掉)
-
怼两次大佬。可以发现,当 \(f_1 +f_2 \leq C_i\) 并且 \(f_1+f_2+(D-d_1-d_2)\geq C_i\) 时可以将大佬怼死。我们把所有二元组按 \(f\) 排序,第一个条件用 two_pointers 维护(对于某个 \(f\) ,能与它配对的二元组是一个连续的区间),至于第二个,我们把第二个指针视作必选的二元组,那么只需要在它可以配对的区间中选一个 \(f-d\) 最大的即可
代码
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 210;
const int MAX_S = 1e6 + 10;
#define Pii pair<int, int>
#define mp make_pair
#define x first
#define y second
int read();
int N, Q, Hp, K;
int T, mxc, top;
int b[MAX_N], h[MAX_N], p[MAX_N];
int f[MAX_N][MAX_N * 10];
struct sta { int i, F, L; };
queue<sta> que;
Pii a[6 * MAX_S], c[MAX_S];
struct Hash_Table {
#define mod 4194303
int cap;
int head[mod + 10];
int nxt[mod + 10], a[mod + 10], b[mod + 10];
Hash_Table() : cap(0) {}
int Hash(Pii pr) {
int x = pr.x, y = pr.y;
return (1LL * x * 20030207 + y) & mod;
}
void insert(Pii pr) {
int u = Hash(pr);
nxt[++cap] = head[u];
head[u] = cap;
a[cap] = pr.x, b[cap] = pr.y;
}
int count(Pii pr) {
int u = Hash(pr);
for (int i = head[u]; i; i = nxt[i])
if (a[i] == pr.x && b[i] == pr.y) return 1;
return 0;
}
#undef mod
} Map;
inline void chkmax(int& x, int y) { x = x > y ? x : y; }
void init() {
for (int i = 1; i <= N; ++i)
for (int j = b[i]; j <= Hp; ++j) {
chkmax(f[i][j - b[i]], f[i - 1][j] + 1),
chkmax(f[i][min(j - b[i] + h[i], Hp)], f[i - 1][j]);
}
for (int i = 1; i <= N; ++i)
for (int j = 0; j <= Hp; ++j) chkmax(T, f[i][j]);
}
void BFS() {
que.push((sta){1, 1, 0});
while (!que.empty()) {
sta fr = que.front(); que.pop();
int I = fr.i, F = fr.F, L = fr.L;
if (I >= T) continue;
que.push((sta){I + 1, F, L + 1});
if (L > 1 && 1LL * L * F <= mxc && !Map.count(mp(1LL * L * F, I + 1))) {
a[++top] = mp(L * F, I + 1);
que.push((sta){I + 1, L * F, L});
Map.insert(a[top]);
}
}
int tp = 0;
sort(a + 1, a + top + 1);
for (int i = 1; i <= top; ++i)
if (a[i].x ^ a[i - 1].x) c[++tp] = a[i];
top = tp;
}
int main() {
N = read(), Hp = read(), K = read();
for (int i = 1; i <= N; ++i) b[i] = read();
for (int i = 1; i <= N; ++i) h[i] = read();
Q = read();
for (int i = 1; i <= Q; ++i) p[i] = read();
for (int i = 1; i <= Q; ++i) chkmax(mxc, p[i]);
init();
BFS();
for (int i = 1; i <= Q; ++i) {
if (!K) {
puts(T >= p[i] ? "Yes" : "No");
continue;
} else if (K == 1) {
if (T >= p[i]) { puts("Yes"); continue; }
int fl = 0;
for (int j = 1; j <= top; ++j) {
if (c[j].x > p[i]) break;
if (T - c[j].y + c[j].x >= p[i]) { fl = 1; break; }
}
puts(fl ? "Yes" : "No");
} else {
if (T >= p[i]) { puts("Yes"); continue; }
int mn = (int)1e9, l = 1, fl = 0;
for (int j = top; j >= 1; --j) {
while (l < j && c[l].x + c[j].x <= p[i]) mn = min(mn, c[l].y - c[l].x), ++l;
if (mn + c[j].y - c[j].x + p[i] <= T) { fl = 1; break; }
if (c[j].x <= p[i] && T - c[j].y + c[j].x >= p[i]) { fl = 1; break; }
}
puts(fl ? "Yes" : "No");
}
}
return 0;
}
int read() {
int x = 0, c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
return x;
}