大 佬 大佬 大佬
题目描述见链接 .
正 解 部 分 \color{red}{正解部分} 正解部分
目标是 活着 使大佬自信心变为 0 0 0,
首先可以发现 一组进攻 在固定的 天数 内, 其产生的效果是相同的,
而 能进攻的天数 越多, 其 “胜算” 越大, 所以考虑先求出 最大活动天数,
这可以使用
d
p
dp
dp 求解, 设
F
[
i
,
j
]
F[i, j]
F[i,j] 表示前
i
i
i 天, 第
i
i
i 天
h
p
hp
hp 为
j
j
j 时能够 活动 的最大天数,
考虑
F
[
i
,
j
]
F[i, j]
F[i,j] 能够更新的状态:
- F [ i + 1 , j − a i + 1 ] = max ( F [ i , j ] + 1 ) F[i+1,j-a_{i+1}] = \max(F[i,j]+1) F[i+1,j−ai+1]=max(F[i,j]+1)
- F [ i + 1 , min ( M C , j − a i + 1 + w i + 1 ) ] = max ( F [ i , j ] ) F[i+1, \min(MC,\ j-a_{i+1}+w_{i+1})] = \max(F[i, j]) F[i+1,min(MC, j−ai+1+wi+1)]=max(F[i,j])
然后取 max ( F [ i , j ] ) \max(F[i,j]) max(F[i,j]) 为最大 活动 天数, 记为 M d Md Md,
注意不是 max ( F [ N , j ] ) \max(F[N, j]) max(F[N,j]), 因为也可以在 N N N 天前击败大佬 .
接下来使用 B F S BFS BFS 配合 哈希表判重 处理出所有的 招式 二元组 ( d , f ) (d, f) (d,f),
由于每次攻击力至少增加 1 1 1 倍, 因此攻击力的状态数是 O ( log C ) O(\log C) O(logC) 级别的, d d d 的状态数是 O ( N ) O(N) O(N) 级别的, 总状态数至少为 O ( N log C ) O(N\log C) O(NlogC), 小于 O ( N 2 ) O(N^2) O(N2), 开 O ( N 2 ) O(N^2) O(N2) 的状态数组即可 .
表示花费 d d d 天, 造成 f f f 的伤害, 且至多含有一次拔刀的招式, 接下来分情况讨论,
- 不拔刀, 需满足 M d ≥ C Md \geq C Md≥C .
- 可能拔一次刀, 需满足 C ≥ f C \geq f C≥f 且 M d − d ≥ C − f Md-d \geq C-f Md−d≥C−f
- 可能拔两次刀, 需满足 C ≥ f 1 + f 2 C \geq f_1 + f_2 C≥f1+f2 且 M d − d 1 − d 2 ≥ C − f 1 − f 2 Md-d_1-d_2 \geq C-f_1-f_2 Md−d1−d2≥C−f1−f2 .
前 2 2 2 种情况很好判断, 考虑如何处理第 3 3 3 个情况,
按 f f f 为第一关键字 从小到大 排序, 从右向左 枚举指针 i i i 表示 ( f 1 , d 1 ) (f_1, d_1) (f1,d1), 然后再维护一个指针 j j j 从左向右 移动, 表示 ( f 2 , d 2 ) (f_2, d_2) (f2,d2), 移动过程中始终保持 C ≥ f 1 + f 2 C \geq f_1 + f_2 C≥f1+f2,
目的是满足条件:
M
d
−
d
1
−
d
2
≥
C
−
f
1
−
f
2
Md-d_1-d_2 \geq C-f_1-f_2
Md−d1−d2≥C−f1−f2
→
\rightarrow
→
M
d
−
d
1
+
f
1
−
C
≥
d
2
−
f
2
Md-d_1+f_1-C \geq d_2-f_2
Md−d1+f1−C≥d2−f2
因此在移动的过程中 在满足
f
1
+
f
2
≤
C
f_1 + f_2 \le C
f1+f2≤C 的前提下 记录
m
i
n
n
=
min
(
d
2
−
f
2
)
minn = \min(d_2-f_2)
minn=min(d2−f2),
由于
f
1
f_1
f1 不断减小, 因此前面合法的
m
i
n
n
minn
minn, 在当前也一定合法,
于是直接检查
M
d
−
d
1
+
f
1
−
C
≥
m
i
n
n
Md-d_1+f_1-C \geq minn
Md−d1+f1−C≥minn 这个条件是否满足, 若满足, 说明至多拔两次刀可以砍掉大佬 .
实 现 部 分 \color{red}{实现部分} 实现部分
在 B F S BFS BFS 更新状态时, 有 2 2 2 种选择,
- 升级时无需将当前状态加入 二元组 集合 .
- 加攻击力时 判重 后加入 B F S BFS BFS队列 和 二元组 集合 .
注意在判重时可以只判断 ( d , f ) (d, f) (d,f) 是否重复, 无需管等级 .
#include<bits/stdc++.h>
#define reg register
#define fi first
#define se second
typedef std::pair<int, int> pr;
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
const int maxn = 1015;
int N;
int M;
int Mc;
int Md;
int Max_c;
int sk_cnt;
int a[maxn];
int w[maxn];
int C[maxn];
int F[maxn][maxn];
struct Hash_Map{
int num0;
int mmod;
int head[maxn*maxn];
struct EDGE{ int nxt, x, y; } edge[maxn*maxn];
void insert(int x ,int y){
int from = (1ll*x*29 + y) % mmod;
edge[++ num0] = (EDGE){ head[from], x, y };
head[from] = num0;
}
bool count(int x, int y){
int from = (1ll*x*29 + y) % mmod;
for(reg int i = head[from]; i; i = edge[i].nxt)
if(edge[i].x == x && edge[i].y == y) return 1;
return 0;
}
} Mp;
std::pair <int, int> skil[maxn*maxn];
struct Node{ int d, f, lev; } ;
void BFS(){
std::queue <Node> Q; Q.push((Node){ 1, 1, 0 });
while(!Q.empty()){
Node ft = Q.front(); Q.pop();
if(ft.d >= Md) continue ;
Q.push((Node){ ft.d+1, ft.f, ft.lev + 1});
if(ft.lev<=1 || 1ll*ft.f*ft.lev > Max_c || Mp.count(ft.d+1, ft.f*ft.lev)) continue ;
Q.push((Node){ ft.d+1, ft.f*ft.lev, ft.lev });
Mp.insert(ft.d+1, ft.f*ft.lev);
skil[++ sk_cnt] = pr(ft.f*ft.lev, ft.d+1);
}
}
void Work(int x){
int minn = 0x7f7f7f7f;
if(C[x] <= Md){ puts("1"); return ; }
for(reg int i = sk_cnt, j = 1; i >= 1; i --){
if(C[x] >= skil[i].fi && Md-skil[i].se >= C[x]-skil[i].fi){ puts("1"); return ; }
while(j <= sk_cnt && skil[i].fi+skil[j].fi <= C[x]){
minn = std::min(minn, skil[j].se - skil[j].fi);
j ++;
}
if(Md - skil[i].se + skil[i].fi - C[x] >= minn){ puts("1"); return ; }
}
puts("0");
}
int main(){
N = read(), M = read(), Mc = read();
for(reg int i = 1; i <= N; i ++) a[i] = read();
for(reg int i = 1; i <= N; i ++) w[i] = read();
for(reg int i = 1; i <= M; i ++) Max_c = std::max(Max_c, C[i] = read());
for(reg int i = 1; i <= N; i ++)
for(reg int j = a[i]; j <= Mc; j ++){
F[i][j-a[i]] = std::max(F[i][j-a[i]], F[i-1][j] + 1);
int &t = F[i][std::min(Mc, j-a[i]+w[i])]; t = std::max(t, F[i-1][j]);
}
for(reg int i = 1; i <= N; i ++)
for(reg int j = 1; j <= Mc; j ++) Md = std::max(Md, F[i][j]);
Mp.mmod = 1e6 + 7; BFS();
std::sort(skil+1, skil+sk_cnt+1);
for(reg int i = 1; i <= M; i ++) Work(i);
return 0;
}