MX-J24 题解(T1 - T4) - 指南

T1: P14056 【MX-X21-T1】[IAMOI R5] 七休制

题目描述

你有三种类型的日子:

  • 加训:疲劳度 + 1 +1 +1
  • 休息:疲劳度不变。
  • 颓废:疲劳度 − 1 -1 1

最开始的疲劳度是 0 0 0,总共有 a + b + c a + b + c a+b+c 天,需要恰好有 a a a 天加训, b b b 天休息, c c c 天颓废。你可以随意安排顺序,求有多少天的疲劳度为 0 0 0

数据范围 0 ≤ a , b , c ≤ 100 0 \le a, b, c \le 100 0a,b,c100

思路

算法标签:贪心

按照贪心的角度来讲,我们先休息 b b b 天(对答案的贡献为 b b b)。然后让加训和颓废交替进行(对答案的贡献为 min ⁡ ( a , c ) \min(a, c) min(a,c))。最终答案为 b + min ⁡ ( a , c ) b + \min(a, c) b+min(a,c)

Code

#include <bits/stdc++.h>
  using namespace std;
  using ll = long long;
  using ull = unsigned long long;
  using db = double;
  using pii = pair<int, int>;
    using pq = priority_queue<int, vector<int>>;
      int main() {
      ios::sync_with_stdio(false);
      cin.tie(nullptr);
      int a, b, c;
      cin >> a >> b >> c;
      cout << b + min(a, c) << "\n";
      return 0;
      }

T2: P14057 【MX-X21-T2】[IAMOI R5] 空气蛹

题目描述

n n n 个杯子,编号为 1 1 1 n n n,每个的容量都为 m m m。现在,第 i i i 个杯子里水的体积为 a i a_i ai

你可以进行若干次操作,每次可以选择两个不同的杯子 i i i j j j ,并把 i i i 中的所有水倒入 j j j 中。如果操作后 j j j 中的水的体积大于 m m m,那么 j j j 中的水的体积会溢出到只剩 m m m

完成这些操作后,你需要保证杯中水的体积单调不减,且留下水的总体积尽量大。求这个最大值。

数据范围 1 ≤ T ≤ 10 1 \le T \le 10 1T10 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1n105 1 ≤ m ≤ 1 0 9 1 \le m \le 10^9 1m109 0 ≤ a i ≤ m 0 \le a_i \le m 0aim

思路

算法标签:贪心

首先如果这些杯子已经按照升序排序排好了,那么答案显然为 ∑ i = 1 n a i \sum\limits_{i = 1}^{n}{a_i} i=1nai

否则,我们至少需要合并一次。由于合并一次就会产生一个空杯子,这样这个杯子就可以作为中转杯,让其他所有的水杯升序排序且不浪费。

那么问题就变成了该让那两个杯子合并呢?贪心来讲显然是最小的两个杯子,因为这样才可以让浪费尽可能的小。

Code

#include <bits/stdc++.h>
  using namespace std;
  using ll = long long;
  using ull = unsigned long long;
  using db = double;
  using pii = pair<int, int>;
    const int N = (int)1e5 + 5;
    int n;
    ll m, a[N], sum = 0;
    void sol() {
    cin >> n >> m;
    bool flag = true;
    sum = 0;
    for (int i = 1; i <= n; i++) {
    cin >> a[i];
    sum += a[i];
    if (a[i] < a[i - 1]) flag = false;
    }
    if (!flag) {
    sort(a + 1, a + n + 1);
    cout << sum - a[1] - a[2] + min(a[1] + a[2], m) << "\n";
    } else cout << sum << "\n";
    }
    int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T; cin >> T;
    while (T--) sol();
    return 0;
    }

T3: P14058 【MX-X21-T3】[IAMOI R5] 两个人的演唱会

题目描述

给定一个长度为 n n n 的,由正整数组成的环 a 1 , a 2 , ⋯   , a n a_1, a_2, \cdots, a_n a1,a2,,an,你需要将这个环切成若干段,是的所有段的内极差都小于等于 m m m,求分成的最小段数。

数据范围 1 ≤ T ≤ 5 ⋅ 1 0 6 1 \le T \le 5 \cdot 10^6 1T5106 1 ≤ n 1 \le n 1n ∑ n ≤ 3 ⋅ 1 0 7 \sum{n} \le 3 \cdot 10^7 n3107 1 ≤ m , a i ≤ 1 0 9 1 \le m, a_i \le 10^9 1m,ai109

思路

算法标签:贪心

先考虑是一条链的情况,那么直接贪心找到最少次数(subtask 7)。

如果是环,我们可以直接暴力枚举从哪里破环,然后进行贪心,这样的时间复杂度是 O ( n 2 ) O(n^2) O(n2)

考虑优化,先破环成链,先特判整个环的极差 ≤ m \le m m 的情况(答案为 1 1 1,然后进行贪心。假设贪心后的最优答案为 a n s ans ans,那么我们的答案就是 a n s 2 \frac{ans}{2} 2ans,原因如下:

  • 假设 ∣ a 1 − a n ∣ > m |a_1 - a_n| > m a1an>m 时,就跟链的情况一样,所以答案为 a n s 2 \frac{ans}{2} 2ans
  • 假设 ∣ a 1 − a n ∣ ≤ m |a_1 - a_n| \le m a1anm 时, a 1 a_1 a1 a n a_n an 必定会合并成为一个段。答案为 ⌊ a n s 2 ⌋ \lfloor\frac{ans}{2}\rfloor 2ans,由于是整数,所以就是 a n s 2 \frac{ans}{2} 2ans

Code

#include <bits/stdc++.h>
  using namespace std;
  using ll = long long;
  using ull = unsigned long long;
  using db = double;
  using pii = pair<int, int>;
    const int N = (int)3e7 + 5;
    int n, m;
    int a[N * 2];
    void init() {
    }
    void sol() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) { // 注意:破环成链
    cin >> a[i];
    a[i + n] = a[i];
    }
    // 贪心求出最小的答案
    int maxn = a[1], minn = a[1], ans = 1;
    for (int i = 2; i <= 2 * n; i++) {
    maxn = max(maxn, a[i]);
    minn = min(minn, a[i]);
    if (maxn - minn > m) {
    ans++;
    maxn = a[i];
    minn = a[i];
    }
    }
    ans /= 2;
    cout << (ans == 0 ? 1 : ans) << "\n"; // 这里包含了一个特判:环的极差 <= m
    }
    int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T; cin >> T;
    while (T--) sol();
    return 0;
    }

T4: P14059 【MX-X21-T4】[IAMOI R5] 使一颗心免于哀伤

题目描述

一个黑白棋子组成的环,双方轮流删除一段连续的同色棋子(知更鸟只能删黑棋,星期日只能删白棋),删后仍成环。如果只剩一种颜色,游戏结束:若全白则知更鸟胜,若全黑则星期日胜。

思路

算法标签:分类讨论

首先特判两种显然的情况:

  1. 环上只有一种颜色,那么答案显然一定(若是白色则知更鸟胜,若是黑色则星期日胜)。
  2. 换上只有两段(两段色块),那么答案一定是先手必胜(手画画就知道了,先手直接全拿完自己的那一段就胜了)。

因为两个人都是聪明的,所以他们不希望出现我拿完一段棋子就只剩下一种颜色的棋子了,所以我们尽量少拿,也就是每次拿一个。这样就很容易想到判断谁必胜的方法:两个人能拿的棋子更多者,为必胜者。

注意:需要先特判那两种情况

Code

#include <bits/stdc++.h>
  using namespace std;
  using ll = long long;
  using ull = unsigned long long;
  using db = double;
  using pii = pair<int, int>;
    const int N = (int)1e5 + 5;
    int n;
    string s;
    void init() {
    }
    void sol() {
    cin >> n;
    cin >> s;
    s = '_' + s;
    // 判断只有一种颜色的情况
    bool flag1 = 0, flag2 = 0;
    for (int i = 1; i <= n; i++) {
    if (s[i] == '1') flag1 = true;
    else flag2 = true;
    }
    if ((flag1 & flag2) == 0) { // 如果只有一种颜色
    cout << (!flag1 ? "Robin\n" : "Sunday\n");
    return ;
    }
    // 数出有多少个色块(注意环)
    int cnt = 1;
    for (int i = 1; i < n; i++) {
    if (s[i] != s[i + 1]) cnt++;
    }
    if (cnt & 1) cnt--; // 首尾颜色相同
    if (cnt == 2) { // 如果只有两段,那么先手必胜(即 Robin)
    cout << "Robin\n";
    return ;
    }
    // 计算 cnt > 2 的情况
    int sum1 = 0, sum2 = 0;
    for (int i = 1; i <= n; i++) {
    if (s[i] == '1') sum1++;
    else sum2++;
    }
    cout << (sum1 > sum2 ? "Robin\n" : "Sunday\n");
      }
      int main() {
      ios::sync_with_stdio(false);
      cin.tie(nullptr);
      int T; cin >> T;
      while (T--) sol();
      return 0;
      }
posted @ 2025-10-02 13:06  wzzkaifa  阅读(18)  评论(0)    收藏  举报