题目
题目原文链接:codeforces.com/contest/204…
对于一个任意的网格,机器人定义其美丽度为网格中所有元素的和。
机器人给你一个长度为 n 的数组 a 和一个长度为 m 的数组 b。你需要构建一个 n×m 的网格 M,使得对于所有 1≤i≤n 和 1≤j≤m,都有 Mi,j=ai⋅bj。
然后,机器人给你 q 个查询,每个查询包含一个整数 x。对于每个查询,判断是否存在一种操作方式,使得在执行以下操作一次后,网格 M 的美丽度为 x:
- 选择整数 r 和 c,满足 1≤r≤n 且 1≤c≤m。
- 将所有满足 i=r 或 j=c 的元素 Mi,j设为0。
注意,查询是独立的,即你在每个查询中并不实际修改网格中的元素 —— 你只需要判断是否存在这样的 r 和 c,使得如果执行上述操作,网格的美丽度将变为 x。另外,即使原始网格的美丽度已经是 x,你仍然必须执行一次操作。
输入
第一行包含三个整数 n、m 和 q,分别表示数组 a 的长度、数组 b 的长度以及查询的数量。
第二行包含 n 个整数 a1,a2,…,an,其中 0≤∣ai∣≤n。
第三行包含 m 个整数 b1,b2,…,bm,其中 0≤∣bj∣≤m。
接下来的 q 行,每行包含一个整数 x,其中 1≤∣x∣≤2×105,表示你希望通过将某一行和某一列的所有元素设为 0 后网格的美丽度。
输出
对于每个查询,如果存在一种操作方式使得网格的美丽度为 x,则输出 "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=4 和 c=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=3 和 c=5,网格的美丽度为 -3。
在第三个查询中,选择 r=3 和 c=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;
}
思路
我们将矩阵的美丽度记为 B, 将数组 a 中所有元素的和记为 SumA, 数组 a 中所有元素的和记为SumB。在执行任何操作之前,矩阵的美丽度可以表示为: B=b1⋅a1+b1⋅a2+b1⋅a3+b2⋅a1+b2⋅a2+…
通过提取公因式,可以将其化简为:
B=b1⋅(a1+a2+a3+…)+b2⋅(a1+a2+a3+…)+…
进一步化简为:
B=(a1+a2+a3+a4+…)⋅(b1+b2+b3+…)
因此,可以写作:
B=SumA⋅SumB
现在,考虑对某一列 C 进行操作的影响。矩阵的美丽度将减少 bc⋅SumA。 类似地,当对某一行 R 进行操作时,矩阵的美丽度将减少 ar⋅SumB。 需要注意的是,位于位置 (r,c) 的元素被重复计算了两次,因此需要在公式中对这一点进行修正。 在考虑上述修正后,设操作后的美丽度为 X。 根据以上观察,可以得到以下公式:
X=SumA⋅SumB−(bi⋅SumA+aj⋅SumB−aj⋅bi)
即:
X=SumA⋅SumB−bi⋅SumA−aj⋅SumB+aj⋅bi
提取公因式后,最终化简为:
X=(SumA−aj)⋅(SumB−bi)
因此,对于每个查询,我们只知道是否存在满足上面条件的ai和bi即可判定。
考虑到每个查询值x 的取值范围为1≤∣x∣≤2⋅105,因此符合条件的 SumA−aj 和 SumB−bi 一定满足1≤∣SumA−aj∣,∣SumB−bi∣≤2⋅105。因此我们可以用数组来记录每个符合条件的 SumA−aj 和 SumB−bi ,然后遍历计算出每个范围落在[1,2×105]的(SumA−aj)⋅(SumB−bi)。经过这些预处理后,对于每个查询,只需要查表即可用O(1)的时间获得答案。
复杂度分析
时间复杂度
-
初始计算:
- 计算 sumA 和 sumB:O(n+m)。
-
预处理:
-
计算 dAr 和 dBc:O(n+m)。
-
枚举差值组合计算乘积:
- 外层循环遍历差值最多 MAX_DIFF 次;
- 内层循环中,乘积 P=dAr×dBc 超过最大查询值后立即停止。因此,总复杂度近似为 O(MAX_DIFF×log(MAX_DIFF))。
-
对于每个 diffA, 内层循环最多执行 ⌊diffAMAX_DIFF−1⌋ 次。
总迭代次数: TotalIterations=∑diffA=1MAX_DIFF-1⌊diffAMAX_DIFF−1⌋
为了简化分析,我们可以近似认为:
⌊diffAMAX_DIFF−1⌋≈diffAMAX_DIFF
因此:
Totalterations≈∑diffA=1MAX_DIFFdiffAMAX_DIFF=MAX_DIFF×∑diffA=1MAX_DIFFdiffA1
调和级数 ∑k=1Nk1 的增长趋势近似为 ln(N)+γ, 其中 γ 是欧拉-马歇罗尼常数(约0.577)。 因此:
∑diffA=1MAX_DIFFdiffA1≈ln(MAX_DIFF)+γ≈O(ln(MAX_DIFF))
因此,总时间复杂度为:
O(MAX_DIFF×ln(MAX_DIFF))
-
查询处理:
- 每个查询的复杂度为 O(1),总共 q 个查询:O(q)。
总时间复杂度:
O(n+m+MAX_DIFF×log(MAX_DIFF)+q)=O(MAX_DIFF×log(MAX_DIFF))
空间复杂度
6 个大小为 MAX_DIFF 的数组记录差值和乘积。因此,总空间复杂度为:O(MAX_DIFF)。