ARTICLE · 1132110
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:
若 Δ < 0,则该方程无实数解,输出 NO;否则 Δ ≥ 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;}四题考点总结
💡 年份点评:2023 年 T1 送分、T2 贪心经典、T3 大模拟(数学输出格式)、T4 首次考到分层图最短路,整体难度为历年最高之一。核心收获:贪心的"只买必需量"思想、把时间模 k 当作状态维度的分层图技巧、以及"大模拟必须逐条核对输出规则"的严谨性。
本文真题基于 2023 CSP-J 第二轮认证官方试卷整理,答案与解析供学习参考,最终以 CCF 官方发布为准。祝各位同学复赛顺利!