231004 考试总结 // 都可以大眼

发布时间 2023-11-15 14:44:14作者: XSC062

这篇文章不是用来谴责题目的…… 只是吐槽一下这个题解,讲得和某些人一样。

我:这个 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