2023 CSP-J 总结

发布时间 2023-10-22 17:14:57作者: liruixiong0101

普及组还好,给下午的提高组涨了涨信心,预估分数:\(100+100+100+20=320\),实际分数不知道。

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=b^2-4ac,x=\frac{-b\pm\sqrt{\Delta}}{2a} \]

\(\Delta<0\) 时,方程无解。

接着,当 \(\Delta\ge 0\) 时,方程的两个解分别为:

\[x_1=\frac{-b}{2a}+\frac{\sqrt{\Delta}}{2a},x_2=\frac{-b}{2a}-\frac{\sqrt{\Delta}}{2a} \]

很明显当 \(a<0\) 时,\(x_1>x_2\),否则 \(x_1<x_2\)
我们将得到的结果可以简化成如下形式:

\[x=\frac{p_1}{q_1}+\frac{p_2\cdot\sqrt{d}}{q_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

难死我了,不写了。


这次比赛除了第四题,其实都还好(就是我做出来的题目好),寄。