2025-11-03 NOIP 模拟赛总结
分数:\(0 + 0 + 40 + 50 = 90\)
永康喵喵栽在了 T1,又双叒叕翻车了!
不过,我的翻车正说明了这套题的质量比较高——都是那种乍一看毫无思路,但只要加一点思维就能迎刃而解的题目,还是挺有意思的。
T1
首先,为了考虑问题方便,我们可以先把所有的 \(a_i\) 都减去 \(a_1\)。
可以先考虑全单调递增时的情况:对于一个 \(k\),如果要在位置为 \(i\) 处匹配上,则必然要满足 \(\frac{a_i}{i} = k\)。但是现在数列有上升、下降、不变三种变化方式,该怎么办呢?我们可以考虑给刚才那个式子的分母做文章。我们可以声明一个数组 \(b\),它满足:
接着我们可以维护一个 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。但是由于本人太蒟蒻了并未做出,回头进步之后一定会补(呱呱语)。

浙公网安备 33010602011771号