231004 考试总结 // 都可以大眼
这篇文章不是用来谴责题目的…… 只是吐槽一下这个题解,讲得和某些人一样。
我:这个 Fe(NO₃)₂ 分解的式子怎么配啊。
智力:啊,你随便猜一个。/ Cindy:这个,你用大眼观察法。
A. 情景剧
http://222.180.160.110:1024/contest/4273/problem/1
对于 \(a_i\),假设它是 \([X_i, Y_i]\) 内的最大值,且 \([X_i, Y_i]\) 是该条件下的极大区间;
相似地,对于 \(a_j\),假设它是 \([P_j, Q_j]\) 内的最小值,且 \([P_j, Q_j]\) 是该条件下的极大区间;
则对于 \(a_i\) 作为区间内最大值,\(a_j\) 作为区间内最小值的情况,满足该条件的区间为 \([\max(X_i, P_j), \min(Y_i, Q_j)]\),答案为 \(k=a_i \times a_j \times [\min(Y_i, Q_j) - \max(X_i, P_j) + 1]\)。我们的目标就是最大化 \(k\)。
两个不固定的值,并且不能拆给斜优做,所以考虑将其中一个变得「固定」。
观察数组,我们发现,对于数组中的最大值 \(a_m\),有 \(X_m = 1,Y_m=n\)。那么此时选取 \(a_m\) 作为区间最大值,选取任意数 \(a_j\) 作为最小值。这样做可以保证由 \(i\) 带来的影响都是最优的,只用枚举 \(j\) 并求解即可。此时的答案就是 \(a_m\times a_j\times (Q_j-P_j + 1)\),直接可求了,真不错。
接下来我们需要求解 \(P_j\) 和 \(Q_j\)。看这个数据范围,应该是 \(\mathcal O(n)\) 的算法。我们不难想到单调栈,可惜我不会,所以我打了同样是 \(\mathcal O(n)\) 的悬线法(我的博客:有关悬线法的介绍)。
悬线求出 \(P_j, Q_j\),长度与 \(a_j\) 和 \(a_m\) 相乘就是答案…… 吗?
答案是大样例会挂掉。于是我打了个二分去跑大样例,发现二分也是错的 只好按照题意模拟,拍了几组过后发现了问题。
我们上述条件成立的前提是 \(i\) 在 \([P_j, Q_j]\) 内且 \(j\) 在 \([X_i, Y_i]\) 内。当 \(i\) 取 \(m\) 时后者显然成立,但很容易构造出来情况让前者不成立。比如说 15 1 5
,当 \(j=3\) 时就会得到错误的答案。
这条路就走死了吗?还没有!这是一个开拓点,我们考虑取 \([P_j, Q_j]\) 内的最大值作为 \(i\),而非全局最大值。因为 \(a_i\) 是区间内最大值,显然有 \(X_i\le P_j\le Q_j \le Y_j\) 不然为什么叫区间内最大值 此时的答案就是区间内最优。
那么怎么求 \([P_j,Q_j]\) 内的最大值呢?当遇到这种求解区间与左右端点一致,并且待求满足可加性的情况时,我们可以在悬线的时候一起求解。当悬线跨越一个区间时,我们直接用这个已求解区间的最大值更新当前求的最大值。
左右分别求最大值(注意要用两个数组分别记录,防止错误更新),最后取两者较大作为最终区间内最大值即可。
最终时间复杂度 \(\mathcal O(n)\)。
🌼 鲜花
不知道为什么听别人说很卡,不卡啊,我一个点 250ms。
啊什么你们打的 \(\mathcal O(n\log n)\)?因为单调栈信息断层不能维护区间内最大值?菜。那我必须把这篇发出来嘲讽你们了。兔子说要是不打 fread
Lemon 上就会起飞,我说我打了,我还疑惑 \(\mathcal O(n)\) 跑 2e6
普通快读怎么会寄呢。一问,啊,带 \(\log\),菜。
啊什么谭委员带 \(\log\) 一个点只要 500ms?那也比我慢,菜。
说起来这是我头一次用悬线解决不在矩阵上,还要维护信息的题,之前并没有细想过单调栈和悬线可维护的信息差异,这次算是误打误撞做对了。
注意到数据范围,应该要开 __int128
吧。
#define int __int128
namespace XSC062 {
using namespace fastIO;
const int maxn = 2e6 + 5;
int a[maxn];
int n, mx, res;
int l[maxn], r[maxn];
int mxl[maxn], mxr[maxn];
int max(int x, int y) {
return x > y ? x : y;
}
int main() {
read(n), l[0] = 1;
for (int i = 1; i <= n; ++i) {
read(a[i]), l[i] = i;
mxl[i] = mxr[i] = a[i];
while (l[i] > 1 &&
a[l[i] - 1] >= a[i]) {
mxl[i] = max(mxl[i],
mxl[l[i] - 1]);
l[i] = l[l[i] - 1];
}
}
r[n + 1] = n;
for (int i = n; i; --i) {
r[i] = i;
while (r[i] < n &&
a[r[i] + 1] >= a[i]) {
mxr[i] = max(mxr[i],
mxr[r[i] + 1]);
r[i] = r[r[i] + 1];
}
res = max(res, max(mxl[i], mxr[i])
* a[i] * (r[i] - l[i] + 1));
}
print(res, '\n');
return 0;
}
} // namespace XSC062
#undef int
B. 抽卡
http://222.180.160.110:1024/contest/4273/problem/2
C. 修改 01 序列
http://222.180.160.110:1024/contest/4273/problem/3
题意转化成寻找一个 \(x\),使得 \(i\bmod d\ne x\) 的所有 \(a_i\) 的和最小。
那我们直接统计一下就好了呀!为什么会出现在 S 组呢,就算是拿来当 T1 也不合适呀,不如和隔壁 J 组 T3 换换位置。真是奇怪。
时间复杂度 \(\mathcal O(n)\)。
namespace XSC062 {
using namespace fastIO;
const int maxn = 2e5 + 5;
int n, d, res, lim;
int a[maxn], f[maxn], s[maxn];
int min(int x, int y) {
return x < y ? x : y;
}
int main() {
read(n), read(d);
for (int i = 1; i <= n; ++i)
read(a[i]);
for (int i = 1; i <= n + d; ++i)
s[i] = s[i - 1] + a[i];
for (int i = 1; i <= n + d; ++i) {
if (i > d)
f[i % d] += s[i - 1] - s[i - d];
else f[i % d] += s[i - 1];
}
res = n + 1;
for (int i = 0; i < d; ++i)
res = min(res, f[i]);
print(res, '\n');
return 0;
}
} // namespace XSC062
—— · EOF · ——
真的什么也不剩啦 😖