网格美丽度调整

62 阅读2分钟

题目

题目原文链接:codeforces.com/contest/204…

对于一个任意的网格,机器人定义其美丽度为网格中所有元素的和。
机器人给你一个长度为 nn 的数组 aa 和一个长度为 mm 的数组 bb。你需要构建一个 n×mn \times m 的网格 MM,使得对于所有 1in1 \leq i \leq n1jm1 \leq j \leq m,都有 Mi,j=aibjM_{i,j} = a_i \cdot b_j

然后,机器人给你 qq 个查询,每个查询包含一个整数 xx。对于每个查询,判断是否存在一种操作方式,使得在执行以下操作一次后,网格 MM 的美丽度为 xx

  1. 选择整数 rrcc,满足 1rn1 \leq r \leq n1cm1 \leq c \leq m
  2. 将所有满足 i=ri = rj=cj = c 的元素 Mi,j设为0M_{i,j} 设为 0

注意,查询是独立的,即你在每个查询中并不实际修改网格中的元素 —— 你只需要判断是否存在这样的 rrcc,使得如果执行上述操作,网格的美丽度将变为 xx。另外,即使原始网格的美丽度已经是 xx,你仍然必须执行一次操作。

输入

第一行包含三个整数 nnmmqq,分别表示数组 aa 的长度、数组 bb 的长度以及查询的数量。
第二行包含 nn 个整数 a1,a2,,ana_1, a_2, \ldots, a_n,其中 0ain0 \leq |a_i| \leq n
第三行包含 mm 个整数 b1,b2,,bmb_1, b_2, \ldots, b_m,其中 0bjm0 \leq |b_j| \leq m
接下来的 qq 行,每行包含一个整数 xx,其中 1x2×1051 \leq |x| \leq 2 \times 10^5,表示你希望通过将某一行和某一列的所有元素设为 0 后网格的美丽度。

输出

对于每个查询,如果存在一种操作方式使得网格的美丽度为 xx,则输出 "YES"(不区分大小写),否则输出 "NO"(不区分大小写)。

示例

输入

3 3 6
-2 3 -3
-2 2 -1
-1
1
-2
2
-3
3

输出

NO
YES
NO
NO
YES
NO

输入

5 5 6
1 -2 3 0 0
0 -2 5 0 -3
4
-3
5
2
-1
2

输出

YES
YES
YES
YES
NO
YES

说明

在第二个示例中,构建的网格为:

0 -2   5  0  -3
0  4 -10  0   6
0 -6  15  0  -9
0  0   0  0   0
0  0   0  0   0

通过选择 r=4r = 4c=2c = 2 执行操作后,网格变为:

0  0   5  0  -3
0  0 -10  0   6
0  0  15  0  -9
0  0   0  0   0
0  0   0  0   0

此时网格的美丽度为 4,因此输出 "YES"。
在第二个查询中,选择 r=3r = 3c=5c = 5,网格的美丽度为 -3。
在第三个查询中,选择 r=3r = 3c=3c = 3,网格的美丽度为 5。

预处理+哈希

代码

#include <bits/stdc++.h>
using namespace std;
using llong = long long;

static constexpr int MAX_DIFF = 200001;

void easyDemonProblem() {
    int numberOfRows, numberOfColumns, numberOfQueries;
    cin >> numberOfRows >> numberOfColumns >> numberOfQueries;

    vector<int> arrayA(numberOfRows);
    vector<int> arrayB(numberOfColumns);

    llong sumA = 0, sumB = 0;

    for (int &ai: arrayA) {
        cin >> ai;
        sumA += ai;
    }

    for (int &bj: arrayB) {
        cin >> bj;
        sumB += bj;
    }

    vector<bool> positiveDiffA(MAX_DIFF, false);
    vector<bool> negativeDiffA(MAX_DIFF, false);
    vector<bool> positiveDiffB(MAX_DIFF, false);
    vector<bool> negativeDiffB(MAX_DIFF, false);
    vector<bool> possiblePositiveProducts(MAX_DIFF, false);
    vector<bool> possibleNegativeProducts(MAX_DIFF, false);

    for (int i = 0; i < numberOfRows; ++i) {
        llong difference = sumA - arrayA[i];
        if (abs(difference) < MAX_DIFF) {
            if (difference < 0) {
                negativeDiffA[-difference] = true;
            } else {
                positiveDiffA[difference] = true;
            }
        }
    }

    for (int j = 0; j < numberOfColumns; ++j) {
        llong difference = sumB - arrayB[j];
        if (abs(difference) < MAX_DIFF) {
            if (difference < 0) {
                negativeDiffB[-difference] = true;
            } else {
                positiveDiffB[difference] = true;
            }
        }
    }

    for (int diffA = 1; diffA < MAX_DIFF; ++diffA) {
        for (int diffB = 1; diffB < MAX_DIFF; ++diffB) {
            const llong prodDiffAB = static_cast<llong>(diffA) * diffB;
            if (prodDiffAB >= MAX_DIFF) {
                break;
            }
            if (positiveDiffA[diffA] and positiveDiffB[diffB] or negativeDiffA[diffA] and negativeDiffB[diffB]) {
                possiblePositiveProducts[prodDiffAB] = true;
            }
            if (positiveDiffA[diffA] and negativeDiffB[diffB] or negativeDiffA[diffA] and positiveDiffB[diffB]) {
                possibleNegativeProducts[prodDiffAB] = true;
            }
        }
    }

    while (numberOfQueries--) {
        llong queryValue;
        cin >> queryValue;
        if (queryValue > 0 and possiblePositiveProducts[queryValue] or
            queryValue < 0 and possibleNegativeProducts[-queryValue]) {
            cout << "YES";
        } else {
            cout << "NO";
        }
        cout << '\n';
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    easyDemonProblem();
    return 0;
}

思路

我们将矩阵的美丽度记为 BB, 将数组 aa 中所有元素的和记为 SumA\operatorname { S u m A }, 数组 aa 中所有元素的和记为SumB\operatorname { SumB }。在执行任何操作之前,矩阵的美丽度可以表示为: B=b1a1+b1a2+b1a3+b2a1+b2a2+B = b _ { 1 } \cdot a _ { 1 } + b _ { 1 } \cdot a _ { 2 } + b _ { 1 } \cdot a _ { 3 } + b _ { 2 } \cdot a _ { 1 } + b _ { 2 } \cdot a _ { 2 } + \ldots

通过提取公因式,可以将其化简为:

B=b1(a1+a2+a3+)+b2(a1+a2+a3+)+B = b _ { 1 } \cdot \left( a _ { 1 } + a _ { 2 } + a _ { 3 } + \ldots \right) + b _ { 2 } \cdot \left( a _ { 1 } + a _ { 2 } + a _ { 3 } + \ldots \right) + \ldots

进一步化简为:

B=(a1+a2+a3+a4+)(b1+b2+b3+)B = \left( a _ { 1 } + a _ { 2 } + a _ { 3 } + a _ { 4 } + \ldots \right) \cdot \left( b _ { 1 } + b _ { 2 } + b _ { 3 } + \ldots \right)

因此,可以写作:

B=SumASumBB = S u m A \cdot S u m B

现在,考虑对某一列 CC 进行操作的影响。矩阵的美丽度将减少 bcSumAb _ { c } \cdot \operatorname { S u m A }。 类似地,当对某一行 RR 进行操作时,矩阵的美丽度将减少 arSumBa_ { r } \cdot \operatorname { S u m B }。 需要注意的是,位于位置 (r,c)( r , c ) 的元素被重复计算了两次,因此需要在公式中对这一点进行修正。 在考虑上述修正后,设操作后的美丽度为 XX。 根据以上观察,可以得到以下公式:

X=SumASumB(biSumA+ajSumBajbi)X = \operatorname { S u m A } \cdot \operatorname { S u m B } - \left( b _ { i } \cdot \operatorname { S u m A } + a _ { j } \cdot \operatorname { S u m B } - a _ { j } \cdot b _ { i } \right)

即:

X=SumASumBbiSumAajSumB+ajbiX = \operatorname { S u m A } \cdot \operatorname { S u m B } - b _ { i } \cdot \operatorname { S u m A } - a _ { j } \cdot \operatorname { S u m B } + a _ { j } \cdot b _ { i }

提取公因式后,最终化简为:

X=(SumAaj)(SumBbi)X = \left( \operatorname { S u m A } - a _ { j } \right) \cdot \left( \operatorname { S u m B } - b _ { i } \right)

因此,对于每个查询,我们只知道是否存在满足上面条件的aia_ibib_i即可判定。

考虑到每个查询值xx 的取值范围为1x21051 \leq | x | \leq 2 \cdot 1 0 ^ { 5 },因此符合条件的 SumAaj\operatorname { S u m A } - a _ { j }SumBbi\operatorname { S u m B } - b _ { i } 一定满足1SumAaj,SumBbi21051\leq\left| \operatorname { S u m A } - a _ { j } \right|, \left| \operatorname { S u m B } - b _ { i } \right|\leq 2 \cdot 1 0 ^ { 5 }。因此我们可以用数组来记录每个符合条件的 SumAaj\operatorname { S u m A } - a _ { j }SumBbi\operatorname { S u m B } - b _ { i } ,然后遍历计算出每个范围落在[1,2×105]\left[1, 2 \times 1 0 ^ { 5 }\right](SumAaj)(SumBbi)\left( \operatorname { S u m A } - a _ { j } \right) \cdot \left( \operatorname { S u m B } - b _ { i } \right)。经过这些预处理后,对于每个查询,只需要查表即可用O(1)O(1)的时间获得答案。

复杂度分析

时间复杂度

  1. 初始计算

    1. 计算 sumA\text{sumA}sumB\text{sumB}O(n+m)O(n + m)
  2. 预处理

    1. 计算 dArdA_rdBcdB_cO(n+m)O(n + m)

    2. 枚举差值组合计算乘积:

      • 外层循环遍历差值最多 MAX_DIFF\text{MAX\_DIFF} 次;
      • 内层循环中,乘积 P=dAr×dBcP = dA_r \times dB_c 超过最大查询值后立即停止。因此,总复杂度近似为 O(MAX_DIFF×log(MAX_DIFF))O(\text{MAX\_DIFF} \times \log(\text{MAX\_DIFF}))
      • 对于每个 diffA\text{diffA}, 内层循环最多执行 MAX_DIFF1diffA\lfloor \frac { \text{MAX\_DIFF} -1} { \text{diffA} } \rfloor 次。

        总迭代次数: TotalIterations=diffA=1MAX_DIFF-1MAX_DIFF1diffAT o t a l I t e r a t i o n s = \sum _ { \text{diffA} = 1 } ^ { \text{MAX\_DIFF-1} } \lfloor \frac { \text{MAX\_DIFF} - 1 } { \text{diffA} } \rfloor

        为了简化分析,我们可以近似认为:

        MAX_DIFF1diffAMAX_DIFFdiffA\lfloor \frac { \mathrm { M A X\_ D I F F }-1 } { \mathrm { d i f f A } } \rfloor \approx \frac { \mathrm { M A X\_D I F F } } { \mathrm { d i f f A } }

        因此:

        TotalterationsdiffA=1MAX_DIFFMAX_DIFFdiffA=MAX_DIFF×diffA=1MAX_DIFF1diffAT o t a l t e r a t i o n s \approx \sum _ { \text{diffA} = 1 } ^ { \text{MAX\_DIFF} } \frac { \text{MAX\_DIFF} } { \text{diffA} } = \text{MAX\_DIFF} \times \sum _ { \text{diffA} = 1 } ^ { \text{MAX\_DIFF} } \frac { 1 } { \text{diffA} }

        调和级数 k=1N1k\sum _ { k = 1 } ^ { N } \frac { 1 } { k } 的增长趋势近似为 ln(N)+γ\ln ( N ) + \gamma, 其中 γ\gamma 是欧拉-马歇罗尼常数(约0.577)。 因此:

        diffA=1MAX_DIFF1diffAln(MAX_DIFF)+γO(ln(MAX_DIFF))\sum _ { \text{diffA} = 1 } ^ { \text{MAX\_DIFF} } \frac { 1 } { \text{diffA} } \approx \ln ( \mathrm { M A X\_ D I F F } ) + \gamma \approx O ( \ln ( \mathrm { M A X \_D I F F } ) )

        因此,总时间复杂度为:

        O(MAX_DIFF×ln(MAX_DIFF))O ( MAX\_ DIFF \times \ln ( M A X\_ D I F F ) )

  3. 查询处理

    • 每个查询的复杂度为 O(1)O(1),总共 qq 个查询:O(q)O(q)

总时间复杂度

O(n+m+MAX_DIFF×log(MAX_DIFF)+q)=O(MAX_DIFF×log(MAX_DIFF))O(n + m + \text{MAX\_DIFF} \times \log(\text{MAX\_DIFF}) + q)\\=O(\text{MAX\_DIFF} \times \log(\text{MAX\_DIFF}))

空间复杂度

6 个大小为 MAX_DIFF\text{MAX\_DIFF} 的数组记录差值和乘积。因此,总空间复杂度为:O(MAX_DIFF)O(\text{MAX\_DIFF})