2023 CSP-J 总结
普及组还好,给下午的提高组涨了涨信心,预估分数:\(100+100+100+20=320\),实际分数:\(100+100+100+0=300\)。
T1:小苹果 / apple
题目描述
小 Y 的桌子上放着 \(n\) 个苹果从左到右排成一列,编号为从 \(1\) 到 \(n\)(\(n\le 10^9\))。
小苞是小 Y 的好朋友,每天她都会从中拿走一些苹果。
每天在拿的时候,小苞都是从左侧第 \(1\) 个苹果开始、每隔 \(2\) 个苹果拿走 \(1\) 个苹果。随后小苞会将剩下的苹果按原先的顺序重新排成一列。
小苞想知道,多少天能拿完所有的苹果,而编号为 \(n\) 的苹果是在第几天被拿走的?
思路:
对于第一问,我们可以思考每一次操作会拿走多少个苹果,很明显若当前有 \(m\) 个苹果,每一次操作拿走 \(\lceil\frac{m}{3}\rceil\) 个苹果,我们只需要 \(O(1)\) 模拟每一次操作即可,考虑计算时间复杂度就是一共要进行几次操作,这样的时间复杂度大概是 \(O(\log_{\frac{3}{2}} n)\)。
对于第二问,我们可以思考,当剩余苹果还有 \(m\) 时,\(m\) 满足什么的情况可以拿走最后一个苹果,很明显当 \(m\equiv 1\pmod 3\) 时可以拿走,照样模拟即可。
代码:
#include <bits/stdc++.h>
using namespace std;
int n, m, ans1, ans2;
int main() {
freopen("apple.in", "r", stdin);
freopen("apple.out", "w", stdout);
cin >> n;
for (m = n; m; ans1++, m -= (m + 2) / 3) {
}
for (m = n; m && (m + 2) % 3; ans2++, m -= (m + 2) / 3) {
}
cout << ans1 << ' ' << ans2 + 1;
return 0;
}
时间复杂度:\(O(\log_{\frac{3}{2}} n)\),空间复杂度:\(O(1)\),时间:\(\text{20 min}\)
T2:公路 / road
题目描述
小苞准备开着车沿着公路自驾。
公路上一共有 \(n\) 个站点,编号为从 \(1\) 到 \(n\)。其中站点 \(i\) 与站点 \(i + 1\) 的距离为 \(v_i\) 公里。
公路上每个站点都可以加油,编号为 \(i\) 的站点一升油的价格为 \(a_i\) 元,且每个站点只出售整数升的油。
小苞想从站点 \(1\) 开车到站点 \(n\),一开始小苞在站点 \(1\) 且车的油箱是空的。已知车的油箱足够大,可以装下任意多的油,且每升油可以让车前进 \(d\) 公里。问小苞从站点 \(1\) 开到站点 \(n\),至少要花多少钱加油?
对于所有数据满足 \(1 \leq n \leq 10^5\),\(1 \leq d \leq 10^5\),\(1 \leq v_i \leq 10^5\),\(1 \leq a_i \leq 10^5\)。
思路:
很明显我们可以将在 \(i\) 站点加的油,移到 \(j\) 站点加,前提是 \(j\le i\),所以我们可以统计加一升油的价格的前缀最小值,然后模拟每一次需要花多少升油,加上价钱,由于只能买整数升油,所以可能会有余下的路程,不要忘记统计余下的路程了。注意,要开 long long
。
代码:
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 1e5 + 5;
long long n, d, now, ans, v[kMaxN], a[kMaxN];
int main() {
freopen("road.in", "r", stdin);
freopen("road.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> d;
for (int i = 1; i < n; i++) {
cin >> v[i];
}
a[0] = 1e18;
for (int i = 1; i <= n; i++) {
cin >> a[i], a[i] = min(a[i], a[i - 1]);
}
for (int i = 1; i < n; i++) {
ans += (v[i] - now + d - 1) / d * a[i];
now = (v[i] - now + d - 1) / d * d - (v[i] - now);
}
cout << ans;
return 0;
}
时间复杂度:\(O(n)\),空间复杂度:\(O(n)\),时间:\(\text{30 min}\)
T3:一元二次方程 / uqe
题目描述:
\(T\) 组数据,每组数据给你一个一元二次方程:\(ax^2+bx+c=0\),若这个方程无解,输出 NO
,否则按输出两个解中最大的那个。
对于所有数据有:\(1 \leq T \leq 5000\),\(1 \leq M \leq 10 ^ 3\),\(|a|,|b|,|c| \leq M\),\(a \neq 0\)。
思路:
我们知道对于一个一元二次方程 \(ax^2+bx+c=0\) 的求根公式如下:
当 \(\Delta<0\) 时,方程无解。
接着,当 \(\Delta\ge 0\) 时,方程的两个解分别为:
很明显当 \(a<0\) 时,\(x_1>x_2\),否则 \(x_1<x_2\)。
我们将得到的结果可以简化成如下形式:
\(\frac{p_1}{q_1}\) 为 \(\frac{-b}{2a}\) 约分得来的,后面的是 \(\sqrt{\Delta}\) 简化,和约分得来的。
输出这个值即可。
代码:
#include <bits/stdc++.h>
using namespace std;
int T, m, up, a, b, c, p1, q1, p2, q2, d, G;
// p1/q1+p2*sqrt(d)/q2
void W(int &a, int &b) {
G = __gcd(a, b);
a /= G, b /= G;
b < 0 && (a = -a, b = -b);
}
int main() {
freopen("uqe.in", "r", stdin);
freopen("uqe.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(0);
for (cin >> T >> m, up = sqrt(5 * m * m) + 114; T; T--) {
cin >> a >> b >> c;
p1 = -b, q1 = 2 * a, d = b * b - 4 * a * c, q2 = 2 * a, p2 = 1;
if (d < 0) {
cout << "NO\n";
continue;
}
if (d) {
for (int i = up; i; i--) {
if (d % (i * i) == 0) {
d /= i * i, p2 = i;
break;
}
}
} else {
d = 1, p2 = 0;
}
a < 0 && (q1 = -q1, p1 = -p1, q2 = -q2);
if (d == 1) {
int e = p1 + p2, f = q1;
W(e, f);
if (!e) {
cout << "0\n";
} else {
f == 1 && (cout << e << '\n');
f != 1 && (cout << e << '/' << f << '\n');
}
continue;
}
W(p1, q1), W(p2, q2);
p1 && q1 == 1 && (cout << p1 << '+');
p1 && q1 != 1 && (cout << p1 << '/' << q1 << '+');
p2 == 1 && q2 == 1 && (cout << "sqrt(" << d << ")\n");
p2 == 1 && q2 != 1 && (cout << "sqrt(" << d << ")/" << q2 << '\n');
p2 != 1 && q2 == 1 && (cout << p2 << "*sqrt(" << d << ")\n");
p2 != 1 && q2 != 1 && (cout << p2 << "*sqrt(" << d << ")/" << q2 << '\n');
}
return 0;
}
时间复杂度:\(O(T\cdot M)\),空间复杂度:\(O(1)\),时间:\(\text{40 min}\)
T4:旅游巴士 / bus
难死我了,不写了。
这次比赛除了第四题,其实都还好(就是我做出来的题目好),寄。