一言(ヒトコト)

P90241 [炼石计划--NOIP模拟五]--打怪兽

题面 小 D 在玩一个打怪兽的游戏,这个游戏的地图可以看做一个 n 个点 m 条边的无向图,在除了一号点之外的所有点上分别有一个等级为 $w_i$ 的怪兽。小 D 开始时处于 1 号节点,等级为 $w_1$

每天开始后,小 D 从 1 号节点开始,经过若干没有怪兽的节点到达一个有怪兽的节点 u,并击杀 u 节点的怪兽。玩家只能击杀等级比自己严格更低的怪兽,且每天只能击杀一个怪兽。
小 D 每击杀一个怪兽,他的等级会升高 A,而每天开始时,所有存活怪兽的等级会升高 B。小 D 想知道他至少需要多少天才能击杀n 节点的怪兽并通关,如果永远不能完成,输出 −1

额 容我思考一下
还没写完我回来了!
来这里的都是同营人吧

分成两种情况

  • A<=B
    人越变越菜(原话
  • A>B
    额怎么形容?人会变强?

整体思路

游戏规则:从1号点出发,每天可以选择击杀一个怪兽(等级严格低于当前等级),击杀后等级升高A,每天结束时所有存活怪兽等级升高B。目标是击杀n号点的怪兽。

关键变量

  • dis[i]:击杀i号点怪兽的最短天数
  • L[i]:击杀i号点怪兽所需的最早天数
    剩下的应该都知道~

A ≤ B 时

这时遇到一个可以打的怪 我们就赶紧打(可以收获微乎其微的A ,更为重要的是现在不打以后可能打不过了
那么我们相当于可以做一个bfs(因为路是一样长的
如果一个点不能被打败(那么他永远都不可能被打败,因为他只会越来越强,那么这个点就走不了,这里不要遍历
我们用bfs求出到达每个点的最小天数
到n的最小天数就是答案

额 我们怎么去看一个点可以不可以被打败?(看下方
对于当前节点u,已经花了dis[u]天到达并击杀u:

  • 玩家等级:w[1] + dis[u] * A(击杀了dis[u]个怪兽)
  • 注意:在BFS路径上,每个节点都代表一次成功击杀

考虑邻居v,需要在第dis[u]+1天击杀v:

  • 玩家等级:w[1] + dis[u] * A
  • 怪兽v等级:w[v] + (dis[u]+1) * B
  • 击杀条件:w[1] + dis[u] * A > w[v] + (dis[u]+1) * B
  • 化简:w[1] + dis[u] * (A-B) - B > w[v]
    其实这个地方我好像推不出来 等我明天去问老师
    推出来了 到v的时候等级还是打败u的时候

这部分的代码

点击查看代码
  for(int i=1;i<=n;i++) dis[i]=0x3f3f3f3f,vis[i]=false;
    queue<int>q;
    dis[1]=0;
    vis[1]=true;
    q.push(1);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=h[u];~i;i=ne[i]){
            int v=e[i];
            if(vis[v]) continue;
            if(w[1]+(A-B)*dis[u]-B>w[v]){//这就是上面的柿子
                vis[v]=true;//能打败才继续走这个点
                dis[v]=dis[u]+1;
                q.push(v);
            }
        }
    }

A > B 时

感觉这部分比上面的难欸
还是类似于dij(和上面有点像
主要看怎么维护这个堆,什么点可以走

Notice

  • L[i]:表示玩家需要至少多少天才能击杀怪兽i
  • L[i] = (w[i] - w[1] + A) / (A - B) + 1
    推导:
    • 第t天玩家等级:w[1] + A*t
    • 怪兽i等级:w[i] + B*t
    • 需要:w[1] + A*t > w[i] + B*t
    • 即:(A-B)*t > w[i] - w[1]
    • 所以:t > (w[i] - w[1]) / (A-B)
    • 取整后加1得到L[i]

类Dij的过程

  1. 每次取出堆最小的节点u
  2. 更新:dis[v] = min(dis[v], max(dis[u]+1, L[v]))
    • 这表示:从u到v至少要花dis[u]+1天,但必须等到第L[v]天才能击杀v
  3. 特殊情况:如果cur < dis[u]-1就把这个点设为inf
    cur 的作用就是标记当前已经处理到第几天了

这部分的代码

点击查看代码
for(int i=1;i<=n;i++) dis[i]=0x3f3f3f3f,vis[i]=false;
    for(int i=2;i<=n;i++){
        L[i]=(w[i]-w[1]+A)/(A-B)+1;
        if(L[i]<=0) L[i]=1;
    }
    priority_queue<PII,vector<PII>,greater<PII>>q;
    dis[1]=0;
    q.push({0,1});
    int cur=-1;
    while(!q.empty()){
        PII t=q.top();q.pop();
        int u=t.second;
        if(vis[u]) continue;
        vis[u]=true;
        if(cur<dis[u]-1){
            dis[u]=0x3f3f3f3f;
            continue;
        }
        cur++;
        for(int i=h[u];~i;i=ne[i]){
            int v=e[i];
            if(vis[v]) continue;
            int nd=max(dis[u]+1,L[v]);
            if(nd<dis[v]){
                dis[v]=nd;
                q.push({dis[v],v});
            }
        }
    }

好像要加快读?
那给一个很快的快读板子吧(是真的很快

点击查看代码
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int read(){
    int x=0,f=1;
    char ch=nc();
    while(ch<48||ch>57){
        if(ch=='-') f=-1;
        ch=nc();
    }
    while(ch>=48&&ch<=57){
        x=x*10+ch-48;
        ch=nc();
    }
    return x*f;
}
void write(int x){
    if(x<0){
        putchar('-');
        x=-x;
    }
    if(x>9) write(x/10);
    putchar(x%10+'0');
}

posted @ 2026-07-29 17:21  冬冬猫  阅读(11)  评论(0)    收藏  举报