CF1721C Min-Max Array Transformation
Description
给你两个长度为 \(n\) 的数组 \(a\) 和 \(b\),存在一个长度为 \(n\) 的数组 \(d\),满足 \(a_i+d_i=b_i\),让你求对于每个 \(a_i\),找到可能匹配的最小和最大 \(b_i\),输出 \(b_i-a_i\) 的最小和最大可能值。每个 \(a_i\) 求得值相互独立,最小和最大值也相互独立。
Analysis
最小的应该很简单了,就是找到 \(b\) 中第一个不小于 \(a_i\) 的数 \(b_j\),输出 \(b_j - b_i\)。为了实现高效的查找,由于原本给出的 \(a\) 和 \(b\) 即是非降的,可以使用二分查找,c++ 党亦可使用 lower_bound 函数。
难点在找最大,因为原数组 \(a\) 和 \(b\) 非降,且长度均为 \(n\),可以考虑如下思路:
从后向前遍历,我们任然通过二分找到 \(b\) 中第一个不小于 \(a_i\) 的数 \(b_j\),当第一次发现 \(i=j\) 时,则 \(a_i\) 到 \(a_n\) 必须匹配 \(b_i\) 到 \(b_n\),此时 \(a_i\) 到 \(a_n\) 均可与 \(b_n\) 相匹配,但由于 \(d\) 非负,因此 \(a_1\) 到 \(a_{i-1}\) 不可能匹配到 \(b_i\) 到 \(b_n\) 中的任何一个数,记录此时匹配位置 \(lst=i=j\)。
下一次再找到 \(i=j\) 时直接使 \(a_i\) 到 \(a_{lst}\) 与 \(b_{lst}\) 匹配,而 \(a_1\) 到 \(a_{i-1}\) 不可能匹配到 \(b_i\) 到 \(b_{lst}\) 中的任何一个数,再记录新的 \(lst\)。直到遍历到开头。
综上所述,反映了分段思想,即通过分段处理来判断能否匹配、可能匹配的最大值。
Code
此处展示 c++ 党代码。
#include <stdio.h>
#include <algorithm>
const int N = (int)2e5 + 5;
int n, a[N], b[N], c[N];
int main(void) {
int t; for (scanf("%d", &t); t--; ) {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i) scanf("%d", &b[i]);
for (int i = 1; i <= n; ++i) {
int l = std:: lower_bound(b + 1, b + 1 + n, a[i]) - b; //二分
printf("%d ", b[l] - a[i]); //最小直接输出
}
putchar('\n');
int l, lst = n + 1;
for (int i = n; i >= 1; --i) {
l = std:: lower_bound(b + 1, b + 1 + n, a[i]) - b; //二分
if (l == i) { //即 Analysis 中所述 i = j
for (int i = l; i < lst; ++i) c[i] = b[lst - 1] - a[i];
lst = l; //由于是倒序遍历,只能记录答案,后面再输出
}
}
for (int i = 1; i <= n; ++i) printf("%d ", c[i]); putchar('\n'); //打印最大
}
return 0;
}
The end. Thanks.
(路过点点

浙公网安备 33010602011771号