P3724 [AH2017/HNOI2017]大佬 [模拟(?), 单调性]
题目描述见链接 .
目标是 活着 使大佬自信心变为 ,
首先可以发现 一组进攻 在固定的 天数 内, 其产生的效果是相同的,
而 能进攻的天数 越多, 其 “胜算” 越大, 所以考虑先求出 最大活动天数,
这可以使用 求解, 设 表示前 天, 第 天 为 时能够 活动 的最大天数,
考虑 能够更新的状态:
然后取 为最大 活动 天数, 记为 ,
注意不是 , 因为也可以在 天前击败大佬 .
接下来使用 配合 哈希表判重 处理出所有的 招式 二元组 ,
由于每次攻击力至少增加 倍, 因此攻击力的状态数是 级别的, 的状态数是 级别的, 总状态数至少为 , 小于 , 开 的状态数组即可 .
表示花费 天, 造成 的伤害, 且至多含有一次拔刀的招式, 接下来分情况讨论,
- 不拔刀, 需满足 .
- 可能拔一次刀, 需满足 且
- 可能拔两次刀, 需满足 且 .
前 种情况很好判断, 考虑如何处理第 个情况,
按 为第一关键字 从小到大 排序, 从右向左 枚举指针 表示 , 然后再维护一个指针 从左向右 移动, 表示 , 移动过程中始终保持 ,
目的是满足条件:
因此在移动的过程中 在满足 的前提下 记录 ,
由于 不断减小, 因此前面合法的 , 在当前也一定合法,
于是直接检查 这个条件是否满足, 若满足, 说明至多拔两次刀可以砍掉大佬 .
在 更新状态时, 有 种选择,
- 升级时无需将当前状态加入 二元组 集合 .
- 加攻击力时 判重 后加入 队列 和 二元组 集合 .
注意在判重时可以只判断 是否重复, 无需管等级 .
#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;
}