P3724 [AH2017/HNOI2017]大佬 [模拟(?), 单调性]

大 佬 大佬

题目描述见链接 .


正 解 部 分 \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,jai+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, jai+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 MdC .
  • 可能拔一次刀, 需满足 C ≥ f C \geq f Cf M d − d ≥ C − f Md-d \geq C-f MddCf
  • 可能拔两次刀, 需满足 C ≥ f 1 + f 2 C \geq f_1 + f_2 Cf1+f2 M d − d 1 − d 2 ≥ C − f 1 − f 2 Md-d_1-d_2 \geq C-f_1-f_2 Mdd1d2Cf1f2 .

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 Cf1+f2,

目的是满足条件: M d − d 1 − d 2 ≥ C − f 1 − f 2 Md-d_1-d_2 \geq C-f_1-f_2 Mdd1d2Cf1f2 → \rightarrow M d − d 1 + f 1 − C ≥ d 2 − f 2 Md-d_1+f_1-C \geq d_2-f_2 Mdd1+f1Cd2f2
因此在移动的过程中 在满足 f 1 + f 2 ≤ C f_1 + f_2 \le C f1+f2C 的前提下 记录 m i n n = min ⁡ ( d 2 − f 2 ) minn = \min(d_2-f_2) minn=min(d2f2),
由于 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 Mdd1+f1Cminn 这个条件是否满足, 若满足, 说明至多拔两次刀可以砍掉大佬 .


实 现 部 分 \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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值