题解:P14128 [SCCPC 2021] Spicy Restaurant

这边是题目传送门喵!

题意简述

给定一张有 \(n\) 个带权的点和 \(m\) 条无向边,进行 \(q\) 次询问,第 \(i\) 次询问距离点 \(p_i\) 最近且权值不超过 \(a_i\) 的点与 \(p_i\) 之间有几条边。

思路

显然要用到最短路。根据数据范围可以发现,所询问的 \(p_i\)\(a_i\) 最多不超过 \(10^7\) 种,容易想到预处理答案数组。

对于每一个点,可以用 BFS 求出最近的且权值不超过 \(a_i\) 的点,然后对于 \([1,100]\) 中每一个 \(a_i\) 都预处理该点的答案,就可以做出来了。

预处理时只用注意以下几点即可:

  1. 初始化答案数组为无穷大,如果预处理后仍未无穷大以表示无法到达;
  2. 对于权值为 \(a_i\) 时的预处理,最小距离最大也不超过 \(a_i-1\) 时的最小距离,因为不超过 \(a_i-1\) 则必然不会超过 \(a_i\) 的值。

然后就没什么值得注意的了。放代码。

AC 代码:

#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int N=100005,W=105;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
int n,m,T,dis[N][W];
int w[N];
vector<int>g[N];

void bfs(int k) {// k 是辣度值
    queue<int>q;
    while(!q.empty())q.pop();

    for(int i=1;i<=n;i++) {
        dis[i][k]=dis[i][k-1];// 对应第二条
    }

    for(int i=1;i<=n;i++) {
        if(w[i]==k) {
            dis[i][k]=0;
            q.push(i);
        }
    }

    while(!q.empty()) {
        int u=q.front();
        q.pop();
        for(int v:g[u]) {
            if(dis[v][k]>dis[u][k]+1) {
                dis[v][k]=dis[u][k]+1;
                q.push(v);
            }
        }
    }
}

void init() {
    memset(dis,inf,sizeof(dis));
    for(int i=1;i<=100;i++)bfs(i);
}

int main() {
    ios;cin>>n>>m>>T;

    for(int i=1;i<=n;i++)
        cin>>w[i];

    int u,v;
    for(int i=1;i<=m;i++) {
        cin>>u>>v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    init();// 预处理

    int p,a;
    while(T--) {
        cin>>p>>a;
        if(dis[p][a]==inf)cout<<"-1\n";// 无法到达
        else cout<<dis[p][a]<<'\n';
    }

    return 0;
}
posted @ 2025-10-30 16:17  Circle_Table  阅读(2)  评论(0)    收藏  举报