夜雨聆风学习资料网

ARTICLE · 1132110

2023 CSP-J 复赛真题及答案解析(完整版)|四题全解

2023 CSP-J 复赛真题及答案解析(完整版)|四题全解

2023 CSP-J 复赛真题及答案解析(完整版)|四题全解

本文分两部分:前半部分为 2023 CSP-J 第二轮(复赛)完整真题,后半部分为四道题的详细解析与满分 C++ 代码。2023 年复赛 T1 送分、T2 贪心经典、T3 大模拟(根式化简)、T4 首次考到分层图最短路,整体难度是历年较高的一年。建议先通读真题、独立尝试,再对照解析查漏补缺。

获取 2024CSP-J赛真题及解析.pdf

请关注状元编程公众号,回复  20261006csp-j


第一部分:真题原文

考试说明

  • 考试:CSP-J 2023 第二轮认证(入门级)
  • 时间:2023 年 10 月 21 日
  • 四道题:小苹果(apple)、公路(road)、一元二次方程(uqe)、旅游巴士(bus)

T1 小苹果(apple)

【题目描述】

小 Y 的桌子上放着 n 个苹果从左到右排成一列,编号为从 1 到 n。小苞每天都会从中拿走一些苹果。每天在拿的时候,小苞都是从左侧第 1 个苹果开始、每隔 2 个苹果拿走 1 个苹果。随后小苞会将剩下的苹果按原先的顺序重新排成一列。

小苞想知道,多少天能拿完所有的苹果,而编号为 n 的苹果是在第几天被拿走的?

【输入格式】 输入的第一行包含一个正整数 n,表示苹果的总数。

【输出格式】 输出一行包含两个正整数,由空格隔开,分别表示拿走所有苹果所需的天数以及拿走编号为 n 的苹果是在第几天。

【样例】

输入:8输出:5 5

【数据范围】 1 ≤ n ≤ 10⁹。


T2 公路(road)

【题目描述】

小苞准备开着车沿着公路自驾。公路上一共有 n 个站点,编号为从 1 到 n。其中站点 i 与站点 i+1 的距离为 vᵢ 公里。公路上每个站点都可以加油,编号为 i 的站点一升油的价格为 aᵢ 元,且每个站点只出售整数升的油。

小苞想从站点 1 开车到站点 n,一开始小苞在站点 1 且车的油箱是空的。已知车的油箱足够大,可以装下任意多的油,且每升油可以让车前进 d 公里。问小苞从站点 1 开到站点 n,至少要花多少钱加油?

【输入格式】 第一行两个正整数 n 和 d;第二行 n−1 个正整数 v₁, v₂, …, vₙ₋₁(站点间距离);第三行 n 个正整数 a₁, a₂, …, aₙ(各站点油价)。

【输出格式】 输出一行一个正整数,表示从站点 1 开到站点 n 至少要花多少钱加油。

【样例】

输入:5 410 10 10 109 8 9 6 5输出:79

【数据范围】 1 ≤ n ≤ 10⁵,1 ≤ d ≤ 10⁵,1 ≤ vᵢ ≤ 10⁵,1 ≤ aᵢ ≤ 10⁵。


T3 一元二次方程(uqe)

【题目描述】

给定一个一元二次方程的系数 a, b, c,其中 a, b, c 均为整数且 a≠0。你需要判断一元二次方程 ax²+bx+c=0 是否有实数解,并按要求的格式输出。

计算 Δ = b²−4ac:

  1. 若 Δ < 0,则该方程无实数解,输出 NO;
  2. 否则 Δ ≥ 0,此时方程有两解(可能相等),记其中较大者为 x,按格式输出 x。

输出有理数 v 时须遵循以下规则:存在唯一两个整数 p 和 q,满足 q>0,gcd(p,q)=1 且 v=p/q;若 q=1,则输出 {p},否则输出 {p}/{q}。

若 x 为无理数,则 x 可唯一表示为 x = q₁+q₂√r 的形式,其中 q₁,q₂ 为有理数(q₂>0),r 为正整数且 r>1,且不存在正整数 d>1 使 d²|r。按规则依次输出 q₁(若 q₁≠0,输出并再输出一个加号 +)、q₂ 与 sqrt(r) 的组合(如 12*sqrt(3)、3/2+sqrt(5)/2、1+sqrt(2)/2 等)。

【输入格式】 第一行两个正整数 T, M;接下来 T 行,每行三个整数 a, b, c。

【输出格式】 输出 T 行,每行一个字符串,格式如题面所述,中间不含空格。

【样例】

输入:9 10001 -1 0-1 -1 -11 -2 11 5 44 4 11 0 -4321 -3 12 -4 11 7 1输出:1NO1-1-1/212*sqrt(3)3/2+sqrt(5)/21+sqrt(2)/2-7/2+3*sqrt(5)/2

【数据范围】 1 ≤ T ≤ 5000,1 ≤ M ≤ 10³,|a|,|b|,|c| ≤ M,a≠0。


T4 旅游巴士(bus)

【题目描述】

小 Z 打算搭乘旅游巴士去景点旅游。景点的地图共有 n 处地点,在这些地点之间连有 m 条道路。其中 1 号地点为景区入口,n 号地点为景区出口。一天当中景区开门营业的时间记为 0 时刻,从 0 时刻起,每间隔 k 单位时间便有一辆旅游巴士到达景区入口,同时有一辆旅游巴士从景区出口驶离景区。

所有道路均只能单向通行。对于每条道路,游客步行通过的用时均为恰好 1 单位时间。

小 Z 希望乘坐旅游巴士到达景区入口,并沿着自己选择的任意路径走到景区出口,再乘坐旅游巴士离开,这意味着他到达和离开景区的时间都必须是 k 的非负整数倍。小 Z 在旅游巴士离开景区前只想一直沿着景区道路移动,而不想在任何地点(包括景区入口和出口)或者道路上停留。

景区采取了限制客流的方法,对于每条道路均设置了一个"开放时间"aᵢ,游客只有不早于 aᵢ 时刻才能通过这条道路。

请帮助小 Z 设计一个旅游方案,使得他乘坐旅游巴士离开景区的时间尽量地早。

【输入格式】 第一行 3 个正整数 n, m, k;接下来 m 行,每行 3 个非负整数 uᵢ, vᵢ, aᵢ,表示第 i 条道路从地点 uᵢ 出发,到达地点 vᵢ,道路的"开放时间"为 aᵢ。

【输出格式】 输出一行一个整数,表示最早离开景区的时刻。如果不存在符合要求的方案,输出 -1。

【样例】

输入:5 5 31 2 02 5 11 3 03 4 34 5 1输出:6

【数据范围】 2 ≤ n ≤ 10⁴,1 ≤ m ≤ 2×10⁴,1 ≤ k ≤ 100,1 ≤ uᵢ,vᵢ ≤ n,0 ≤ aᵢ ≤ 10⁶。


第二部分:题目解析

T1 小苹果(apple)|取整模拟

  • 考点:数学 / 模拟 / 取整
  • 难度:普及−,送分题

思路:每天从 1 号位置开始,每隔 2 个拿 1 个(即拿走位置 1,4,7,…)。每天拿走的个数 = ⌈n/3⌉;剩余 n = n − ⌈n/3⌉。第 n 个苹果(始终位于当前序列末尾)在某天被拿走 ⟺ 当天剩余数 n % 3 == 1(末尾位置恰在被拿的 1,4,7 序列中)。

易错点:① ⌈n/3⌉ 用整数 (n+2)/3;② 要同时输出"总天数"和"第 n 个苹果被拿的天"。

#include<bits/stdc++.h>usingnamespace std;typedeflonglong ll;intmain(){    ll n;    cin >> n;    ll days = 0, ansDay = 0;while (n > 0) {        days++;if (n % 3 == 1 && ansDay == 0)   // 末尾位置恰在 1,4,7,... 中            ansDay = days;        n -= (n + 2) / 3;                 // 每天拿走 ceil(n/3) 个    }    cout << days << ' ' << ansDay << '\n';return0;}

T2 公路(road)|加油站贪心

  • 考点:贪心 / 模拟
  • 难度:普及,经典贪心模型

思路:经典"加油站问题"变形。走到哪里算到哪里,只在当前已见过的最便宜油价处买"走到当前点所必需"的油。维护 mn(历史最低油价)、bought(已购油量)、累计距离;每到新站,若需油量 need = ⌈累计距离/d⌉ 大于已购量,则以 mn 补足差额;然后更新 mn。

易错点:① 需要向上取整(多买一升不找零、不能欠油);② 若后面油价更低,前面不应多买 → 只买"已走完路程"的必需量。

#include<bits/stdc++.h>usingnamespace std;typedeflonglong ll;constint MAXN = 100005;ll v[MAXN], a[MAXN];intmain(){int n; ll d;    cin >> n >> d;for (int i = 1; i < n; i++) cin >> v[i];for (int i = 1; i <= n; i++) cin >> a[i];    ll ans = 0, bought = 0, dist = 0, mn = a[1];for (int i = 1; i < n; i++) {        dist += v[i];        ll need = (dist + d - 1) / d;      // 走到当前点至少需要的油(向上取整)if (need > bought) {               // 只补"已走路程"的必需量,且按历史最低价买            ans += (need - bought) * mn;            bought = need;        }        mn = min(mn, a[i + 1]);            // 更新历史最低油价    }    cout << ans << '\n';return0;}

T3 一元二次方程(uqe)|根式化简大模拟

  • 考点:数学 / 模拟 / gcd 约分 / 根式化简 / 分类讨论
  • 难度:普及,大模拟题,分类讨论要仔细

思路:

  • Δ = b²−4ac:Δ<0 输出 NO;Δ 为完全平方数 → 有理根,输出约分后的分数(分母为正,整数直接输出)。
  • Δ 非完全平方 → 较大根恒为 (u + v·√r) / w 形式(分母取正后根式系数恒为正)。需要:提出 Δ 的完全平方因子(r 无平方因子)、对 (u, v, w) 三部分整体约分、按省略规则输出(u=0 省略有理部分、v=1 省略系数、w=1 省略分母)。

易错点:① 分母必须化为正;② 较大根对应"正根式"一侧(a<0 时要取 − 号再翻符号);③ 约分要对分子、根式系数、分母三部分同时做;④ 完全平方判定要回乘验证。

#include<bits/stdc++.h>usingnamespace std;typedeflonglong ll;ll gcd(ll x, ll y){ return y ? gcd(y, x % y) : x; }voidprintRational(ll p, ll q){if (q < 0) { p = -p; q = -q; }    ll g = gcd(abs(p), q);    p /= g; q /= g;if (q == 1) cout << p;else cout << p << '/' << q;}intmain(){    ios::sync_with_stdio(false);    cin.tie(nullptr);int T, M;    cin >> T >> M;while (T--) {        ll a, b, c;        cin >> a >> b >> c;        ll delta = b * b - 4 * a * c;if (delta < 0) { cout << "NO\n"; continue; }        ll sq = (ll)sqrt((longdouble)delta);while ((sq + 1) * (sq + 1) <= delta) sq++;while (sq * sq > delta) sq--;if (sq * sq == delta) {            ll num = (a > 0 ? -b + sq : -b - sq);            ll den = 2 * a;if (den < 0) { num = -num; den = -den; }printRational(num, den);            cout << '\n';        } else {            ll r = delta, v = 1;for (ll i = 2; i * i <= r; i++)     // 提出完全平方因子while (r % (i * i) == 0) { r /= i * i; v *= i; }            ll u = -b, w = 2 * a;if (w < 0) { u = -u; v = -v; w = -w; }   // 分母化正(较大根一侧)            ll g1 = gcd(abs(u), w), g2 = gcd(abs(v), w);            u /= g1; v /= g2;            ll w1 = w / g1, w2 = w / g2;if (u != 0) { printRational(u, w1); cout << '+'; }if (v == 1 && w2 == 1) cout << "sqrt(" << r << ')';elseif (v == 1) cout << "sqrt(" << r << ")/" << w2;elseif (w2 == 1) cout << v << "*sqrt(" << r << ')';else cout << v << "*sqrt(" << r << ")/" << w2;            cout << '\n';        }    }return0;}

T4 旅游巴士(bus)|分层图最短路

  • 考点:图论 / 最短路 / 分层图
  • 难度:提高−,六年中 T4 最"图论"的一道

思路:

出发、到达时刻都是 k 的倍数 ⇒ 路径步数 L ≡ 0 (mod k)。

分层图:状态 (u, j) 表示"到达 u 且时刻 mod k = j",dist[u][j] 为最早到达时刻。

转移:边 (u, v, a),当前时刻 t。若 t < a,则只能把出发时刻推迟 k 的整数倍(等效于到达 u 的时刻加 s·k 直到 ≥ a),即 s = ⌈(a−t)/k⌉,通过后到 v 的时刻 = t + s·k + 1,新状态 (v, (t+s·k+1) mod k)。

答案 = dist[n][0](时刻为 k 的倍数)。

易错点:① 不能在某点等待任意时长,只能推迟出发时刻 k 的整数倍(保持模不变);② 状态维度是"时刻 mod k"而非节点;③ 边开放时间用 long long。

#include<bits/stdc++.h>usingnamespace std;typedeflonglong ll;constint MAXN = 10005;const ll INF = 4e18;structEdge { int v; ll a; };vector<Edge> g[MAXN];ll dist[MAXN][105];      // dist[u][j]: 最早到达 u 且时刻 mod k = jint n, m, k;intmain(){    ios::sync_with_stdio(false);    cin.tie(nullptr);    cin >> n >> m >> k;for (int i = 0; i < m; i++) {int u, v; ll a;        cin >> u >> v >> a;        g[u].push_back({v, a});    }for (int i = 1; i <= n; i++)for (int j = 0; j < k; j++) dist[i][j] = INF;    priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> pq;    dist[1][0] = 0;    pq.push({0, 1 * k + 0});while (!pq.empty()) {auto [t, code] = pq.top(); pq.pop();int u = code / k, r = code % k;if (t > dist[u][r]) continue;for (auto &e : g[u]) {            ll add = 0;if (t < e.a) add = (e.a - t + k - 1) / k;            ll nt = t + add * k + 1;int nr = nt % k;if (nt < dist[e.v][nr]) {                dist[e.v][nr] = nt;                pq.push({nt, e.v * k + nr});            }        }    }    cout << (dist[n][0] == INF ? -1 : dist[n][0]) << '\n';return0;}

四题考点总结

题号
题目
核心算法
难度
T1
小苹果
取整模拟
普及−送分
T2
公路
加油站贪心
普及
T3
一元二次方程
根式化简大模拟
普及
T4
旅游巴士
分层图最短路
提高−

💡 年份点评:2023 年 T1 送分、T2 贪心经典、T3 大模拟(数学输出格式)、T4 首次考到分层图最短路,整体难度为历年最高之一。核心收获:贪心的"只买必需量"思想、把时间模 k 当作状态维度的分层图技巧、以及"大模拟必须逐条核对输出规则"的严谨性。


本文真题基于 2023 CSP-J 第二轮认证官方试卷整理,答案与解析供学习参考,最终以 CCF 官方发布为准。祝各位同学复赛顺利!

相关学习资料