2026年9月GESP真题及题解(C++八级):末班车

2026年9月GESP真题及题解(C++八级):末班车

题目描述

城市里有 n n n 个地铁站以及 m m m 条地铁线路,地铁站依次以 1 , 2 , ... , n 1,2,\ldots,n 1,2,...,n 编号。

第 i i i 条地铁线路( 1 ≤ i ≤ m 1\le i\le m 1≤i≤m)的列车从地铁站 u i u_i ui 单向驶向地铁站 v i v_i vi,最晚发车时间为第 l i l_i li 分钟,途中行驶需要 t i t_i ti 分钟。从第 0 0 0 分钟到第 l i l_i li 分钟,每分钟都会有一班列车从地铁站 u i u_i ui 发出。第 x x x 分钟( 0 ≤ x ≤ l i 0\le x\le l_i 0≤x≤li)发出的列车会在第 x + t i x+t_i x+ti 分钟到达地铁站 v i v_i vi,乘坐这班列车的乘客可以换乘第 x + t i x+t_i x+ti 分钟以及之后的所有从地铁站 v i v_i vi 发出的任意线路的列车。

现在有 q q q 组询问。第 i i i 组询问( 1 ≤ i ≤ q 1\le i\le q 1≤i≤q)给出起点地铁站编号 x i x_i xi,终点地铁站编号 y i y_i yi 以及出发时间 s i s_i si,你需要判断第 s i s_i si 分钟从地铁站 x i x_i xi 出发是否能到达地铁站 y i y_i yi。第 s i s_i si 分钟从地铁站 x i x_i xi 出发意味着你可以乘坐第 s i s_i si 分钟以及之后的所有从地铁站 x i x_i xi 发出的任意线路的列车。

输入格式

第一行,三个正整数 n , m , q n,m,q n,m,q,分别表示地铁站数量,地铁线路数量,询问数量。

接下来 m m m 行,每行四个整数 u i , v i , l i , t i u_i,v_i,l_i,t_i ui,vi,li,ti,分别表示地铁线路的起点,终点,最晚发车时间,行驶所需时间。

接下来 q q q 行,每行三个整数 x i , y i , s i x_i,y_i,s_i xi,yi,si,分别表示行程起点,行程终点,出发时间。

输出格式

输出共 q q q 行。对于每组询问,如果第 s i s_i si 分钟从地铁站 x i x_i xi 出发能到达地铁站 y i y_i yi 则输出一行 Yes,否则输出一行 No。请注意输出区分大小写。

输入输出样例 1
输入 1
复制代码
3 4 5
1 2 3 3
2 3 5 2
3 1 4 1
1 3 0 6
1 3 2
2 1 2
2 1 3
3 2 2
3 2 3
输出 1
复制代码
Yes
Yes
No
Yes
No
说明/提示

对于 40 % 40\% 40% 的测试点,保证 q ≤ 100 q\le 100 q≤100。

对于所有测试点,保证:

  • 1 ≤ n ≤ 500 1\le n\le 500 1≤n≤500
  • 1 ≤ m ≤ 1000 1\le m\le 1000 1≤m≤1000
  • 1 ≤ q ≤ 5 × 10 5 1\le q\le 5\times 10^5 1≤q≤5×105
  • 1 ≤ u i , v i , x i , y i ≤ n 1\le u_i,v_i,x_i,y_i\le n 1≤ui,vi,xi,yi≤n
  • 0 ≤ l i , s i ≤ 10 5 0\le l_i,s_i\le 10^5 0≤li,si≤105
  • 1 ≤ t i ≤ 10 4 1\le t_i\le 10^4 1≤ti≤104

思路分析

定义 f[y][x] 表示:若终点为 y,从站点 x 出发,最晚可以在第 f[y][x] 分钟出发并到达 y。

如果询问的出发时间 s <= f[y][x],则可以从 x 等到最晚可行发车时间,因此输出 Yes;否则输出 No。

反向递推:

对于一条边 u -> v,最晚发车时间为 l,耗时为 t。

如果从 u 出发想最终到达 y,那么选择第 k 分钟发车时,需要满足:

  • k <= l
  • 到达 v 的时间是 k + t
  • 从 v 还能到达 y,所以需要 k + t <= f[y][v]

因此:

text 复制代码
k <= min(l, f[y][v] - t)

所以:

text 复制代码
f[y][u] = max( min(l, f[y][v] - t) )

特殊地,f[y][y] = +∞,因为已经在终点。

对每个终点 y 反向建图,从 y 开始用大根堆做类似 Dijkstra 的倒推。由于 t >= 1,越往前倒推,最晚出发时间只会变小,因此当前堆中最大的未确定值就是最终值。

时间复杂度:

O ( n ∗ ( n + m ) l o g n + q ) O(n * (n + m) log n + q) O(n∗(n+m)logn+q)

空间复杂度:

O ( n 2 + m ) O(n^2+m) O(n2+m)

代码实现

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N=505;//最大站点数加5
const int I=1e9;//无穷大,表示终点本身
struct E{int u,l,t;};//反向边:u为起点,l为最晚发车,t为耗时
int f[N][N];//f[y][x]表示终点y,起点x的最晚出发时间
vector<E> g[N];//g[v]存所有指向v的边

int main(){
    ios::sync_with_stdio(false);//关闭同步
    cin.tie(0);//解绑输入
    cout.tie(0);//解绑输出
    
    int n,m,q;//站点数,线路数,询问数
    cin>>n>>m>>q;//读入规模
    for(int i=0;i<m;i++){//读入每条线路
        int u,v,l,t;//起点,终点,最晚发车,耗时
        cin>>u>>v>>l>>t;//读入线路
        g[v].push_back({u,l,t});//反向存储,便于从终点倒推
    }
    
    for(int y=1;y<=n;y++){//枚举每个终点y
        int d[N];//d[u]表示从u到y的最晚出发时间
        for(int i=1;i<=n;i++) d[i]=-1;//初始不可达
        priority_queue<pair<int,int>> p;//大根堆,按最晚时间从大到小
        d[y]=I;//终点本身随时都在
        p.push({I,y});//从终点开始倒推
        while(!p.empty()){//类似Dijkstra
            int w=p.top().first;//当前最晚时间
            int v=p.top().second;//当前站点
            p.pop();//弹出
            if(w!=d[v]) continue;//过期状态
            for(int i=0;i<(int)g[v].size();i++){//遍历所有指向v的边
                int u=g[v][i].u;//边的起点
                int l=g[v][i].l;//边最晚发车
                int t=g[v][i].t;//边耗时
                int c=min(l,w-t);//从u最晚发车时间
                if(c>=0&&c>d[u]){//能更新且非负
                    d[u]=c;//更新
                    p.push({c,u});//入堆
                }
            }
        }
        for(int x=1;x<=n;x++) f[y][x]=d[x];//保存答案
    }
    while(q--){//处理询问
        int x,y,s;//起点,终点,出发时间
        cin>>x>>y>>s;//读入询问
        if(s<=f[y][x]) cout<<"Yes\n";//不晚于最晚出发时间
        else cout<<"No\n";//否则不能
    }
    return 0;
}

功能分析

该程序预处理每个终点 y 到所有起点 x 的最晚可行出发时间 f[y][x]。

对于每次询问 (x, y, s),只需要判断 s <= f[y][x] 是否成立。

成立说明可以从 x 在第 s 分钟或之后出发并最终到达 y,输出 Yes;否则输出 No。

该做法能处理 q 高达 5 × 10 5 5×10^5 5×105 的询问,因为询问部分是 O(1) 的。

【秘籍汇总】(完整csp信奥赛C++学习资料):

1、csp/信奥赛C++,完整信奥赛系列课程(永久学习):

https://edu.csdn.net/lecturer/7901 点击跳转

2、CSP信奥赛C++竞赛拿奖视频课:

https://edu.csdn.net/course/detail/40437 点击跳转

https://edu.csdn.net/course/detail/41081 点击跳转

3、csp信奥赛高频考点知识详解及案例实践:

CSP信奥赛C++动态规划:

https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转

CSP信奥赛C++标准模板库STL:

https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转

信奥赛C++提高组csp-s知识详解及案例实践:

https://blog.csdn.net/weixin_66461496/category_13113932.html 点击跳转

4、csp信奥赛冲刺一等奖有效刷题题解:

信奥赛C++普及组CSP-J一等奖通关刷题题单及题解:

https://blog.csdn.net/weixin_66461496/category_12673810.html 点击跳转

信奥赛C++普及组csp-j初赛&复赛真题题解(持续更新): https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新):

https://blog.csdn.net/weixin_66461496/category_13125089.html 点击跳转

5、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):

https://blog.csdn.net/weixin_66461496/category_13117178.html 点击跳转

· 文末祝福 ·

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"跟着王老师一起学习信奥赛C++";
	cout<<"    成就更好的自己!       ";
	cout<<"  csp信奥赛一等奖属于你!   ";
	return 0;
}
相关推荐
cu1431 小时前
细谈GM7123C的具体功能与其应用
c语言·c++·人工智能·嵌入式硬件
朝朝辞暮i2 小时前
C++ 第 10 课:函数 Function
开发语言·c++
Zootopia6262 小时前
快递无人车与无人机各自进入配送网络后,路线能否一起算?
c++·人工智能·机器学习·matlab·ai·机器人·无人机
朝朝辞暮i2 小时前
C++ 第7课 while 循环
开发语言·c++·算法
huanbailu4 小时前
Qt 跨线程信号槽:connect 返回 true 但槽函数不执行的坑
开发语言·c++·qt·信号槽·跨线程·元类型
HEJOO94 小时前
C++ 虚函数表(vtable)深度解析
开发语言·c++
xailorliu5 小时前
矩阵定性-正定负定不定半正定半负定
c++·opencv·线性代数·计算机视觉·矩阵
个 人 练 习 生5 小时前
C++-----string基本使用详解
开发语言·c++·学习
朝朝辞暮i6 小时前
C++ 第 5 课:else if + 多条件判断 + && / || / !
开发语言·c++