2025-11-03 NOIP 模拟赛总结

分数\(0 + 0 + 40 + 50 = 90\)

永康喵喵栽在了 T1,又双叒叕翻车了!

不过,我的翻车正说明了这套题的质量比较高——都是那种乍一看毫无思路,但只要加一点思维就能迎刃而解的题目,还是挺有意思的。

T1

首先,为了考虑问题方便,我们可以先把所有的 \(a_i\) 都减去 \(a_1\)

可以先考虑全单调递增时的情况:对于一个 \(k\),如果要在位置为 \(i\) 处匹配上,则必然要满足 \(\frac{a_i}{i} = k\)。但是现在数列有上升、下降、不变三种变化方式,该怎么办呢?我们可以考虑给刚才那个式子的分母做文章。我们可以声明一个数组 \(b\),它满足:

\[\forall \ i \in [1, n], \ b_i = \begin{cases} b_{i-1}+1, &a_i > a_{i-1};\\ b_{i-1}, &a_i = a_{i-1};\\ b_{i-1}-1, &a_i < a_{i-1}. \end{cases}\]

接着我们可以维护一个 map,并遍历数组。对于每个 \(i\),我们将 map 下标为 \(\frac{a_i}{b_i}\) 的地方 \(+1\)。最后遍历一遍 map,看看哪里的值最大,把那里的下标作为 \(k\),值作为答案,行云流水,一气呵成,测样例——

……你就会发现自己奇妙地 WA 或 RE 掉了。为什么?我们少考虑了一种 corner:\(\exist \ b_i = 0\)。小学数学告诉我们,\(0\) 不能做除数——于是程序就以 3221225620 的返回值华丽收场了。那怎么办呢?可以注意到,如果 \(b_i = 0\),那么此时无论 \(k\) 是多少都能匹配上。专门特判一下就可以了。

#include <bits/stdc++.h>
#define int long long
const int N = 1e6+10;
int a[N], b[N];
int n, ans, k;
std::map<int, int> mp;
int Abs(int x) {
  return x >= 0 ? x : -x;
}

signed main() {
  freopen("piano.in", "r", stdin);
  freopen("piano.out", "w", stdout);
  std::ios::sync_with_stdio(false); std::cin.tie(0);
  std::cin >> n;
  for (int i = 1; i <= n; i++) {
    std::cin >> a[i];
  }
  for (int i = 2; i <= n; i++) {
    a[i] -= a[1];
  }
  a[1] = 0;
  for (int i = 2; i <= n; i++) {
    if (a[i] > a[i-1]) b[i] = b[i-1] + 1;
    else if (a[i] == a[i-1]) b[i] = b[i-1];
    else b[i] = b[i-1] - 1;
  }
  int equal = 0;
  for (int i = 1; i <= n; i++) {
    if (b[i] == 0) equal += (a[i] == 0);
    else if (a[i] % b[i] == 0 && a[i] / b[i] >= 0) {
      mp[a[i] / b[i]]++;
    }
  }
  for (auto it = mp.begin(); it != mp.end(); it++) {
    if (it -> second > ans) {
      ans = it -> second;
      k = it -> first;
    }
  }
  std::cout << equal + ans << '\n' << k << '\n';
  return 0;
}

T2

在考场看到这题时我就想骂出题人:为什么部分分给的是 \(R, G, B\) 而不是 \(n\)?直到看到了正解,才知道出题人就像还原糖一样是有设计的。

注意到 \(R, G, B\) 非常小,我们可以用略超过 \(R^3\) 的复杂度来解决问题。我们可以把每一支笔看作三位空间里的一个点,然后把 Colorfulness 值当作立方体的棱长。如果这个立方体能覆盖 \(k\) 个点,那么将其棱长就可以作为一个合法的 Colorfulness 值。可以使用三维前缀和轻松获得立方体覆盖的点数。又注意到这道题要求的是“最大值的最小值”,可以想到二分。于是这道题的做法就是:

  • 预处理三维前缀和。
  • 二分棱长。
  • 对于每个棱长,枚举其一个顶点的坐标,然后看能不能覆盖至少 \(k\) 个点。

时间复杂度为 \(O(R^3 \log R)\)。在代码实现中,这里的 \(R\) 实际上为定值 \(255\)

#include <bits/stdc++.h>
typedef long long ll;
const int N = 1e5+10, R = 260;
int n, k, a[R][R][R], preSum[R][R][R];

bool check(int len) {
  for (int sx = 1; sx <= 256; sx++) {
    for (int sy = 1; sy <= 256; sy++) {
      for (int sz = 1; sz <= 256; sz++) {
        int tx = std::min(sx + len, 256), ty = std::min(sy + len, 256), tz = std::min(sz + len, 256);
        int ans = preSum[tx][ty][tz] - preSum[sx-1][ty][tz] - preSum[tx][sy-1][tz] - preSum[tx][ty][sz-1] + preSum[sx-1][sy-1][tz] + preSum[sx-1][ty][sz-1] + preSum[tx][sy-1][sz-1] - preSum[sx-1][sy-1][sz-1];
        if (ans >= k) {
          return true;
        }
      }
    }
  }
  return false;
}

int main() {
  std::ios::sync_with_stdio(false); std::cin.tie(0);
  std::cin >> n >> k;
  for (int i = 1; i <= n; i++) {
    int r, g, b;
    std::cin >> r >> g >> b;
    a[r+1][g+1][b+1]++;
  }
  for (int x = 1; x <= 256; x++) {
    for (int y = 1; y <= 256; y++) {
      for (int z = 1; z <= 256; z++) {
        preSum[x][y][z] = a[x][y][z] + preSum[x-1][y][z] + preSum[x][y-1][z] + preSum[x][y][z-1] - preSum[x-1][y-1][z] - preSum[x-1][y][z-1] - preSum[x][y-1][z-1] + preSum[x-1][y-1][z-1]; // Smelly and long!
      }
    }
  }
  int l = 0, r = 255, mid, ans = 255;
  while (l <= r) {
    mid = (l + r) >> 1;
    if (check(mid)) {
      ans = mid;
      r = mid - 1;
    } else {
      l = mid + 1;
    }
  }
  std::cout << ans << '\n';
  return 0;
}

T3

讲题人说很容易能看出来这是换根 DP。但是由于本人太蒟蒻了并未做出,回头进步之后一定会补(呱呱语)。

posted @ 2025-11-03 16:18  JZ8  阅读(13)  评论(0)    收藏  举报