只要你跑的够快,锅就追不上你

0%

「AHOI / HNOI 2017」大佬(动态规划 + BFS)

题目大意

「AHOI / HNOI 2017」大佬(Luogu 3724)

有 $q$ 个大佬,你要和他们一一对决。对决分 $n$ 天,你的初始自信值为 $m$,大佬的初始自信值为 $C$。如果某一天你将大佬的自信值恰好减到 $0$,并且在这天之前你的自信值都不小于 $0$,那么你就获胜了。

一开始,你的等级 $L$ 为 $0$,讽刺能力 $F$ 为 $1$。在第 $i$ 天,大佬会将你的自信值减少 $a_i$,然后你可以进行下列操作中的一种:

  1. 还一句嘴,使大佬的自信值减去 $1$。
  2. 做一天的水题,使自己的自信值 $+ w_i$。注意当前自信值修改后要对初始生命值取 $\min$。
  3. 使自己的等级 $L$ 增加 $1$。
  4. 使自己的讽刺能力 $F$ 乘上自己的等级 $L$。
  5. 怼大佬,使大佬的自信值减少 $F$。之后 $L, F$ 都回到初始值。这种操作只能进行不超过 $2$ 次。

问对于每个大佬,你是否能够战胜他。

数据范围:$n, m \le 100, q \le 20, C \le 10^8$。

思路分析

首先我们发现获胜的条件可以拆成:保证自己活着,并且在 $n$ 天内将大佬的自信值扣到 $0$。我们先做一个 DP 来看看我们最多可以花多少天来怼大佬。值得注意的是,我们不必需要用完所有的天数,所以我们在任何一天停止都是合法的。下面我们将 $n$ 变为求出的有效天数。

接下来我们考虑如何有效地怼大佬。我们一共可以怼他 $0, 1, 2$ 次。预先进行一次 BFS,求出所有的形如 $(F, D)$ 的有序数对,表示我们使用 $D$ 天可以将讽刺能力增加到 $F$。我们需要用 map 记录访问过的 $(L, F)$ 数对来避免重复访问。暴力 BFS 的规模太大,但是我们发现我们只需要 BFS 到第 $32$ 层即可退出。

之后,对于每一个大佬,我们容易处理出怼他 $0$ 或 $1$ 次是否会成功,接下来考虑怼他两次是否会成功。由于我们要求 $F_a + F_b \le C$,所以使用 two - pointers 的技巧,对于每个合法的对看 $D_a + D_b + C - F_a - F_b \le n$ 是否成立即可。这样,我们已经解决了这个问题。

代码实现

1
#include <cstdio>
2
#include <cstring>
3
#include <map>
4
#include <algorithm>
5
using namespace std;
6
7
const int maxn = 100, maxm = 1e7, inf = 1e8 + 1;
8
int n, q, m, a[maxn + 3], w[maxn + 3], dp[maxn + 3][maxn + 3];
9
int M, c[25], h, t, Qd[maxm + 3], Ql[maxm + 3], Qf[maxm + 3];
10
map<pair<int, int>, bool> vis;
11
map<int, int> mp;
12
int cnt, D[maxm + 3], F[maxm + 3];
13
14
void upd_max(int &x, int y) {
15
	x < y ? x = y : 0;
16
}
17
18
void upd_min(int &x, int y) {
19
	x > y ? x = y : 0;
20
}
21
22
void prework() {
23
	for (int i = 1; i <= n; i++) {
24
		for (int j = a[i]; j <= m; j++) {
25
			dp[i][j - a[i]] = max(dp[i][j - a[i]], dp[i - 1][j] + 1);
26
			upd_max(dp[i][min(m, j - a[i] + w[i])], dp[i - 1][j]);
27
		}
28
	}
29
	int x = 0;
30
	for (int i = 1; i <= n; i++) {
31
		for (int j = 0; j <= m; j++) {
32
			upd_max(x, dp[i][j]);
33
		}
34
	}
35
	n = x;
36
}
37
38
void solve() {
39
	h = t = 1;
40
	Qd[t] = 1, Ql[t] = 0, Qf[t] = 1, t++;
41
	while (h < t) {
42
		int d = Qd[h], l = Ql[h], f = Qf[h];
43
		h++;
44
		if (!mp.count(f)) {
45
			mp[f] = d;
46
		}
47
		if (d >= n || d >= 35) {
48
			continue;
49
		}
50
		Qd[t] = d + 1, Ql[t] = l + 1, Qf[t] = f, t++;
51
		vis[make_pair(l + 1, f)] = true;
52
		if (l > 1 && 1ll * f * l < inf && !vis.count(make_pair(l, f * l))) {
53
			Qd[t] = d + 1, Ql[t] = l, Qf[t] = f * l, t++;
54
			vis[make_pair(l, f * l)] = true;
55
		}
56
	}
57
}
58
59
int main() {
60
	scanf("%d %d %d", &n, &q, &m);
61
	for (int i = 1; i <= n; i++) {
62
		scanf("%d", &a[i]);
63
	}
64
	for (int i = 1; i <= n; i++) {
65
		scanf("%d", &w[i]);
66
	}
67
	for (int i = 1; i <= q; i++) {
68
		scanf("%d", &c[i]);
69
		M = max(M, c[i]);
70
	}
71
	prework();
72
	solve();
73
	for (map<int, int>::iterator it = mp.begin(); it != mp.end(); it++) {
74
		F[++cnt] = it -> first, D[cnt] = it -> second;
75
	}
76
	for (int c, _ = 1; _ <= q; _++) {
77
		c = c[_];
78
		bool flag = c <= n;
79
		for (int i = 1; i <= cnt; i++) {
80
			if (F[i] <= c && D[i] + c - F[i] <= n) {
81
				flag = true;
82
				break;
83
			}
84
		}
85
		int pos = 1;
86
		for (int i = cnt; i; i--) {
87
			while (pos <= cnt && F[i] + F[pos] <= c) {
88
				if (D[i] + D[pos] + c - F[i] - F[pos] <= n) {
89
					flag = true;
90
					break;
91
				}
92
				pos++;
93
			}
94
			if (flag) {
95
				break;
96
			}
97
		}
98
		printf("%d\n", flag);
99
	}
100
	return 0;
101
}