2024信奥赛C++提高组csp-s复赛真题及题解:超速检测

2024信奥赛C++提高组csp-s复赛真题及题解:超速检测

题目描述

小 D 新入职了某国的交管部门,他的第一个任务是负责国家的一条长度为 L L L 的南北主干道的车辆超速检测。为了考考小 D,上司首先需要他解决一个简化的场景。

这个周末,主干道上预计出现 n n n 辆车,其中第 i i i 辆车从主干道上距离最南端 d i d_i di 的位置驶入,以 v i v_i vi 的初速度和 a i a_i ai 的加速度做匀加速运动向北行驶。我们只考虑从南向北的车辆,故 v i > 0 v_i > 0 vi>0,但 a i a_i ai 可正可负,也可以为零。当车辆行驶到主干道最北端(即距离最南端为 L L L 的位置)或速度降为 0 0 0(这只可能在 a i < 0 a_i < 0 ai<0 时发生)时,我们认为该车驶离主干道。

主干道上设置了 m m m 个测速仪,其中第 j j j 个测速仪位于主干道上距离最南端 p j p_j pj 的位置,每个测速仪可以设置开启或关闭。当某辆车经过某个开启的测速仪时,若这辆车的瞬时速度超过 了道路限速 V V V,那么这辆车就会被判定为超速。注意当车辆驶入与驶出主干道时,如果在对应位置有一个开启的测速仪,这个测速仪也会对这辆车进行测速。

上司首先想知道,如果所有测速仪都是开启的,那么这 n n n 辆车中会有多少辆车被判定为超速。

其次,为了节能,部门想关闭一部分测速仪。然而,他们不希望漏掉超速的车,也就是说,当 n n n 辆车里的某辆车在所有测速仪都开启时被判定为超速,他们希望在关闭一部分测速仪以后它依然被判定为超速。上司还想知道在这样的条件下最多可以关闭多少测速仪。

由于 n n n 很大,上司允许小 D 使用编程解决这两个问题,于是小 D 找到了你。

如果你对于加速度并不熟悉,小 D 贴心地在本题的"提示"部分提供了有关加速度的公式。

输入格式

输入的第一行包含一个正整数 T T T,表示数据组数。

接下来包含 T T T 组数据,每组数据的格式如下:

第一行包含四个整数 n , m , L , V n, m, L, V n,m,L,V,分别表示车辆数量、测速仪数量、主干道长度和道路限速。

接下来 n n n 行:

第 i i i 行包含三个整数 d i , v i , a i d_i, v_i, a_i di,vi,ai 描述一辆车。

最后一行包含 m m m 个整数 p 1 , p 2 , ... , p m p_1, p_2, \dots , p_m p1,p2,...,pm 描述道路上所有测速仪的位置。

输出格式

对于每组数据:输出一行包含两个整数,第一个整数为所有测速仪都开启时被判定为超速的车辆数量,第二个整数为在不漏掉超速车辆的前提下最多可以关闭的测速仪数量。

输入输出样例 1
输入 1
复制代码
1
5 5 15 3
0 3 0
12 4 0
1 1 4
5 5 -2
6 4 -4
2 5 8 9 15
输出 1
复制代码
3 3
说明/提示

【样例 1 解释】

在该组测试数据中,主干道长度为 15 15 15,限速为 3 3 3,在距离最南端 2 , 5 , 8 , 9 , 15 2, 5, 8, 9, 15 2,5,8,9,15 的位置各设有一个测速仪。

  • 第一辆车在最南端驶入,以 3 3 3 的速度匀速行驶。这辆车在整个路段上都没有超速。
  • 第二辆车在距离最南端 12 12 12 的位置驶入,以 4 4 4 的速度匀速行驶。在最北端驶离主干道时,它会被距离最南端 15 15 15 的测速仪判定为超速。
  • 第三辆车在距离最南端 1 1 1 的位置驶入,以 1 1 1 的初速度、 4 4 4 的加速度行驶。其在行驶了 3 2 − 1 2 2 × 4 = 1 \frac{3^2-1^2}{2\times 4}=1 2×432−12=1 的距离,即到达 2 2 2 的位置时,速度变为 3 3 3,并在之后一直超速。因此这辆车会被除了距离最南端 2 2 2 的测速仪以外的其他测速仪判定为超速。
  • 第四辆车在距离最南端 5 5 5 的位置驶入,以 5 5 5 的初速度、 − 2 -2 −2 的加速度行驶。其在行驶了 3 2 − 5 2 2 × ( − 2 ) \frac{3^2-5^2}{2\times (-2)} 2×(−2)32−52 的距离,即到达 9 9 9 的位置时,速度变为 3 3 3。因此这辆车在距离最南端 [ 5 , 9 ) [5, 9) [5,9) 时超速,会被距离最南端 5 5 5 和 8 8 8 的两个测速仪判定为超速。
  • 第五辆车在距离最南端 6 6 6 的位置驶入,以 4 4 4 的初速度、 − 4 −4 −4 的加速度行驶。在其行驶了 3 2 − 4 2 2 × ( − 4 ) = 7 8 \frac{3^2-4^2}{2\times (-4)}=\frac{7}{8} 2×(−4)32−42=87 的距离后,即这辆车到达 6 7 8 6\frac{7}{8} 687 的位置时,其速度变为 3 3 3。因此这辆车在距离最南端 [ 6 , 6 7 8 ) [6,6\frac{7}{8}) [6,687) 时超速,但这段区间内没有测速仪,因此不会被判定为超速。

因此第二、三、四辆车会被判定为超速,输出的第一个数为 3 3 3。

我们可以关闭距离最南端 2 , 8 , 9 2, 8, 9 2,8,9 的三个测速仪,保留 5 5 5 和 15 15 15 的两个测速仪,此时三辆之前被判定为超速的车依然被判定为超速。可以证明不存在更优方案,因此输出的第二个数为 3 3 3。

【数据范围】

对于所有测试数据,保证:

  • 1 ≤ T ≤ 20 1 \leq T \leq 20 1≤T≤20;
  • 1 ≤ n , m ≤ 10 5 1 \leq n, m \leq 10^5 1≤n,m≤105, 1 ≤ L ≤ 10 6 1 \leq L \leq 10^6 1≤L≤106, 1 ≤ V ≤ 10 3 1 \leq V \leq 10^3 1≤V≤103;
  • 0 ≤ d i < L 0 \leq d_i < L 0≤di<L, 1 ≤ v i ≤ 10 3 1 \leq v_i \leq 10^3 1≤vi≤103, ∣ a i ∣ ≤ 10 3 |a_i| \leq 10^3 ∣ai∣≤103;
  • 0 ≤ p 1 < p 2 < ⋯ < p m ≤ L 0 \leq p_1 < p_2 < \dots < p_m \leq L 0≤p1<p2<⋯<pm≤L。
测试点 n , m ≤ n,m\leq n,m≤ 特殊性质
1 1 1 10 10 10
2 2 2 20 20 20 ^
3 3 3 3000 3000 3000 A
4 4 4 10 5 10^5 105 ^
5 5 5 3000 3000 3000 B
6 6 6 10 5 10^5 105 ^
7 7 7 3000 3000 3000 C
8 8 8 10 5 10^5 105 ^
9 9 9 3000 3000 3000
10 10 10 10 5 10^5 105 ^

特殊性质 A:保证 a i = 0 a_i = 0 ai=0。

特殊性质 B:保证 a i > 0 a_i > 0 ai>0。

特殊性质 C:保证 a i < 0 a_i < 0 ai<0,且所有车都不在最北端驶离主干道。

【提示】

与加速度有关的定义和公式如下:

  • 匀加速运动是指物体在运动过程中,加速度保持不变的运动,即每单位时间内速度的变化量是恒定的。
  • 当一辆车的初速度为 v 0 v_0 v0、加速度 a ≠ 0 a\neq 0 a=0,做匀加速运动,则 t t t 时刻后它的速度 v 1 = v 0 + a × t v_1 = v_0 + a \times t v1=v0+a×t,它的位移(即行驶路程) s = v 0 × t + 0.5 × a × t 2 s=v_0\times t+0.5\times a\times t^2 s=v0×t+0.5×a×t2。
  • 当一辆车的初速度为 v 0 v_0 v0、加速度 a ≠ 0 a \neq 0 a=0,做匀加速运动,则当它的位移(即行驶路程)为 s s s 时,这辆车的瞬时速度为 v 0 2 + 2 × a × s \sqrt{v_0^2+2\times a\times s} v02+2×a×s 。
  • 当一辆车的初速度为 v 0 v_0 v0、加速度 a ≠ 0 a \neq 0 a=0,在它的位移(即行驶路程)为 v 1 2 − v 0 2 2 a \frac{v_1^2-v_0^2}{2a} 2av12−v02 时,这辆车的瞬时速度为 v 1 v_1 v1。

如果你使用浮点数进行计算,需要注意潜在的精度问题。

思路分析

问题一:计算超速车辆数(所有测速仪开启)

核心思路

  1. 对每辆车,计算其在道路上的行驶区间[d, E],其中E为离开位置的整数部分
  2. 确定该车在哪些测速仪位置会超速:
    • 匀速运动(a=0):若v>V,则在所有经过的测速仪处都超速
    • 加速运动(a>0):从某个位置开始一直超速,直到离开
    • 减速运动(a<0):从进入位置开始减速,直到某个位置速度降到V以下

关键公式

  • 瞬时速度公式:v² = v₀² + 2a·s
  • 超速条件:v > V,即v₀² + 2a·s > V²

实现细节

  • 使用二分查找确定超速区间的起点或终点
  • 避免浮点数运算,全部使用整数运算防止精度误差
  • 注意处理a=0的特殊情况
问题二:计算最多可关闭的测速仪数

问题转化

  • 每辆超速车对应一个测速仪下标区间[l, r]
  • 需要选择最少的测速仪,使得每个区间内至少有一个测速仪被选中
  • 这是一个经典的区间覆盖选点问题

贪心算法

  1. 将所有区间按右端点升序排序
  2. 初始化last = -1(上一个选择的测速仪)
  3. 遍历每个区间:
    • 如果当前区间已被覆盖(last在区间内),则跳过
    • 否则选择当前区间的右端点作为新的测速仪位置
  4. 最终选择的测速仪数量cnt即为最少需要保留的数量

正确性证明

选择右端点可以最大化覆盖后续区间的可能性,这是区间选点问题的标准贪心策略。

代码实现

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

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T;  // 数据组数
    cin >> T;
    while (T--) {
        int n, m, L, V;
        cin >> n >> m >> L >> V;
        
        // 存储车辆信息:位置d,初速度v,加速度a
        vector<tuple<int, int, int>> cars(n);
        for (int i = 0; i < n; ++i) {
            int d, v, a;
            cin >> d >> v >> a;
            cars[i] = {d, v, a};
        }
        
        // 测速仪位置,已按升序排列
        vector<ll> p(m);
        for (int i = 0; i < m; ++i) cin >> p[i];
        
        // segs存储每辆超速车对应的测速仪下标区间 [l, r]
        vector<pair<int, int>> segs;
        
        // 第一问:计算所有测速仪开启时的超速车辆
        for (int i = 0; i < n; ++i) {
            int d = get<0>(cars[i]), v = get<1>(cars[i]), a = get<2>(cars[i]);
            
            // 计算车辆行驶的整数右端点E(最后一个整数位置)
            int E;
            if (a >= 0) {
                E = L;  // 加速度非负,车辆会行驶到最北端
            } else {  // a < 0,车辆可能提前停止
                // 计算速度降为0时行驶的距离
                double s_stop = -1.0 * v * v / (2 * a);
                // 实际离开位置:取到最北端和停止位置的较小值
                double e = min((double)L, d + s_stop);
                E = (int)floor(e);  // 向下取整得到最后一个整数位置
            }
            
            if (E < d) continue;  // 行驶区间为空,跳过
            
            // 二分查找测速仪在[d, E]范围内的下标区间[low, high]
            int low = lower_bound(p.begin(), p.end(), d) - p.begin();
            int high = upper_bound(p.begin(), p.end(), E) - p.begin() - 1;
            if (low > high) continue;  // 该区间内无测速仪
            
            // 计算V² - v²,用于后续判断
            ll target = 1LL * V * V - 1LL * v * v;
            
            if (a == 0) {  // 匀速运动
                if (v > V) {  // 如果速度始终超过限速
                    segs.emplace_back(low, high);
                }
                continue;
            }
            
            if (a > 0) {  // 加速运动
                // 二分查找第一个超速的测速仪位置
                // 条件:2*a*(p[mid]-d) > V² - v²
                int left = low, right = high, ans = -1;
                while (left <= right) {
                    int mid = (left + right) / 2;
                    ll val = 2LL * a * (p[mid] - d);
                    if (val > target) {
                        ans = mid;      // 记录答案
                        right = mid - 1; // 继续向左查找更早的超速点
                    } else {
                        left = mid + 1;  // 当前点未超速,向右查找
                    }
                }
                if (ans != -1) {  // 存在超速区间
                    segs.emplace_back(ans, high);
                }
            } else {  // a < 0,减速运动
                if (v <= V) continue;  // 初速度就不超速,之后速度只会更小
                
                // 二分查找最后一个超速的测速仪位置
                // 条件:2*a*(p[mid]-d) > V² - v²
                // 注意:a<0时,2*a*(p[mid]-d)是负数,且随mid增加而减小
                int left = low, right = high, ans = -1;
                while (left <= right) {
                    int mid = (left + right) / 2;
                    ll val = 2LL * a * (p[mid] - d);
                    if (val > target) {  // 仍然大于目标值,说明还在超速
                        ans = mid;       // 记录答案
                        left = mid + 1;  // 尝试向右查找更远的超速点
                    } else {
                        right = mid - 1;  // 已不超速,向左查找
                    }
                }
                if (ans != -1) {  // 存在超速区间
                    segs.emplace_back(low, ans);
                }
            }
        }
        
        int ans1 = segs.size();  // 超速车辆数量
        
        // 第二问:求最多可以关闭的测速仪数量
        // 问题转化为:选择最少的测速仪,使得每辆超速车的超速区间内至少有一个测速仪被保留
        // 贪心策略:按区间右端点排序,每次选择当前区间的右端点
        sort(segs.begin(), segs.end(), [](const pair<int, int>& a, const pair<int, int>& b) {
            return a.second < b.second;  // 按右端点升序排序
        });
        
        int last = -1;  // 上一个选择的测速仪下标
        int cnt = 0;    // 需要保留的测速仪数量
        for (auto& seg : segs) {
            int l = seg.first, r = seg.second;
            // 如果上一个选择的测速仪在当前区间内,则当前区间已被覆盖
            if (last >= l && last <= r) continue;
            // 否则选择当前区间的右端点
            cnt++;
            last = r;
        }
        
        int ans2 = m - cnt;  // 最多可关闭的测速仪数量
        
        cout << ans1 << " " << ans2 << "\n";
    }
    return 0;
}

功能分析

一、复杂度分析

1.时间复杂度

  • 对每辆车:二分查找O(log m),总复杂度O(n log m)
  • 区间排序:O(k log k),其中k为超速车辆数,k ≤ n
  • 贪心选择:O(k)
  • 总复杂度:O(n log m),满足n,m ≤ 10⁵的要求

2.空间复杂度

  • O(n + m)存储车辆和测速仪信息
  • O(k)存储超速区间,k ≤ n
二、注意事项
  1. 整数运算:完全避免浮点数,使用平方比较
  2. 边界处理:车辆离开位置向下取整,因为测速仪只在整数位置
  3. 空区间判断:当low > high时,表示无测速仪在行驶区间内
  4. 特殊情况
    • a=0时直接判断初速度
    • a<0且v≤V时不可能超速
    • 提前停止的情况(a<0时可能未到最北端就停止)
三、关键优化
  • 使用lower_bound/upper_bound进行二分查找
  • 使用vector和tuple减少内存开销
  • 贪心算法保证最优解

各种学习资料,助力大家一站式学习和提升!!!

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"##########  一站式掌握信奥赛知识!  ##########";
	cout<<"#############  冲刺信奥赛拿奖!  #############";
	cout<<"######  课程购买后永久学习,不受限制!   ######";
	return 0;
}

1、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

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

CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新):https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

CSP信奥赛C++一等奖通关刷题题单及题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12673810.html 点击跳转

3、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

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

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

· 文末祝福 ·

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"跟着王老师一起学习信奥赛C++";
	cout<<"    成就更好的自己!       ";
	cout<<"  csp信奥赛一等奖属于你!   ";
	return 0;
}
相关推荐
Ronaldinho Gaúch4 小时前
leetcode279完全平方数
c++·算法·动态规划
Howrun7774 小时前
C++_bind_可调用对象转化器
开发语言·c++·算法
爱装代码的小瓶子5 小时前
【C++与Linux基础】文件篇(3)-fd的本质和minishell的重定向功能
linux·c++
2301_822382765 小时前
嵌入式C++实时内核
开发语言·c++·算法
Max_uuc5 小时前
【C++ 硬核】拒绝单位混淆:利用 Phantom Types (幻影类型) 实现零开销的物理量安全计算
开发语言·c++
2301_790300965 小时前
C++与物联网开发
开发语言·c++·算法
凤年徐5 小时前
C++ STL list 容器详解:使用与模拟实现
开发语言·c++·后端·list
艾莉丝努力练剑5 小时前
【Linux进程控制(三)】实现自主Shell命令行解释器
linux·运维·服务器·c++·人工智能·安全·云原生
坐怀不乱杯魂6 小时前
Linux网络 - UDP/TCP底层
linux·服务器·网络·c++·tcp/ip·udp