CF1721C Min-Max Array Transformation

题链:cf luogu

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.

(路过点点

posted @ 2022-08-29 17:33  Dry_ice  阅读(188)  评论(0)    收藏  举报