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

大佬

题目描述见链接 .


color{red}{正解部分}

目标是 活着 使大佬自信心变为 00,

首先可以发现 一组进攻 在固定的 天数 内, 其产生的效果是相同的,
而 能进攻的天数 越多, 其 “胜算” 越大, 所以考虑先求出 最大活动天数,
这可以使用 dpdp 求解, 设 F[i,j]F[i, j] 表示前 ii 天, 第 iihphpjj 时能够 活动 的最大天数,
考虑 F[i,j]F[i, j] 能够更新的状态:

  • F[i+1,jai+1]=max(F[i,j]+1)F[i+1,j-a_{i+1}] = max(F[i,j]+1)
  • F[i+1,min(MC, jai+1+wi+1)]=max(F[i,j])F[i+1, min(MC, j-a_{i+1}+w_{i+1})] = max(F[i, j])

然后取 max(F[i,j])max(F[i,j]) 为最大 活动 天数, 记为 MdMd,

注意不是 max(F[N,j])max(F[N, j]), 因为也可以在 NN 天前击败大佬 .

接下来使用 BFSBFS 配合 哈希表判重 处理出所有的 招式 二元组 (d,f)(d, f),

由于每次攻击力至少增加 11 倍, 因此攻击力的状态数是 O(logC)O(log C) 级别的, dd 的状态数是 O(N)O(N) 级别的, 总状态数至少为 O(NlogC)O(Nlog C), 小于 O(N2)O(N^2), 开 O(N2)O(N^2) 的状态数组即可 .

表示花费 dd 天, 造成 ff 的伤害, 且至多含有一次拔刀的招式, 接下来分情况讨论,

  • 不拔刀, 需满足 MdCMd geq C .
  • 可能拔一次刀, 需满足 CfC geq fMddCfMd-d geq C-f
  • 可能拔两次刀, 需满足 Cf1+f2C geq f_1 + f_2Mdd1d2Cf1f2Md-d_1-d_2 geq C-f_1-f_2 .

22 种情况很好判断, 考虑如何处理第 33 个情况,

ff 为第一关键字 从小到大 排序, 从右向左 枚举指针 ii 表示 (f1,d1)(f_1, d_1), 然后再维护一个指针 jj 从左向右 移动, 表示 (f2,d2)(f_2, d_2), 移动过程中始终保持 Cf1+f2C geq f_1 + f_2,

目的是满足条件: Mdd1d2Cf1f2Md-d_1-d_2 geq C-f_1-f_2 ightarrow Mdd1+f1Cd2f2Md-d_1+f_1-C geq d_2-f_2
因此在移动的过程中 在满足 f1+f2Cf_1 + f_2 le C 的前提下 记录 minn=min(d2f2)minn = min(d_2-f_2),
由于 f1f_1 不断减小, 因此前面合法的 minnminn, 在当前也一定合法,
于是直接检查 Mdd1+f1CminnMd-d_1+f_1-C geq minn 这个条件是否满足, 若满足, 说明至多拔两次刀可以砍掉大佬 .


color{red}{实现部分}

BFSBFS 更新状态时, 有 22 种选择,

  • 升级时无需将当前状态加入 二元组 集合 .
  • 加攻击力时 判重 后加入 BFSBFS队列 和 二元组 集合 .

注意在判重时可以只判断 (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;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822428.html