题目描述
难度分:1400
输入n,m(1≤n×m≤105)和n行m列的矩阵a,元素范围[1,105]。
对于矩阵中的所有相同元素对,即满足a[x1][y1]=a[x2][y2]的元素对(a[x1][y1],a[x2][y2]),把|x1−x2|+|y1−y2|加到答案中。
注意(a,b)和(b,a)只算一次,输出答案。
输入样例1
2 3
1 2 3
3 2 1
输出样例1
7
输入样例2
3 4
1 1 2 2
2 1 1 2
2 2 1 1
输出样例2
76
输入样例3
4 4
1 1 2 3
2 1 1 2
3 1 2 1
1 1 2 1
输出样例3
129
算法
前缀和
用哈希表mp构建一个映射“元素值 → 索引列表”,然后遍历每个元素值对答案的贡献。对于一个元素值val,它的索引列表为pos,以pos长度为4为例,此时val对答案的贡献为
|x4−x3|+|x4−x2|+|x4−x1|+|x3−x2|+|x2−x1|+|y4−y3|+|y4−y2|+|y4−y1|+|y3−y2|+|y2−y1|
此时我们发现x和y是独立的,分开来看,并且如果x坐标和y坐标如果有序并不影响答案,就能直接把绝对值拿掉,上式就会被化简为
Σ4i=2((i−1)×xi−Σi−1j=1xj+(i−1)×yi−Σi−1j=1yj)
这样我们先将x坐标和y坐标分成两个数组xs和ys分别排序,然后遍历一遍xs和ys,在这个过程中维护子数组[1,i)的前缀和就能计算出元素val对答案的贡献。依次计算每个元素的贡献,累加起来就能得到答案。
复杂度分析
时间复杂度
构建映射mp的时间复杂度为O(nm),对每个val处理存在排序操作,排序是性能瓶颈,一共对nm个位置进行了排序,时间复杂度为O(nmlog2nm),这也是算法整体的时间复杂度。
空间复杂度
算法需要存储每个元素的位置二元组,而一共有nm个元素,因此算法整体的额外空间复杂度在O(nm)级别。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <array>
#include <vector>
#include <unordered_map>
using namespace std;
typedef long long LL;
int n, m, a;
LL get(vector<array<int, 2>>& tup) {
int sz = tup.size();
vector<LL> xs, ys;
for(int i = 0; i < sz; i++) {
xs.push_back(tup[i][0]);
ys.push_back(tup[i][1]);
}
sort(xs.begin(), xs.end());
sort(ys.begin(), ys.end());
LL res = 0, presumX = xs[0], presumY = ys[0];
for(int i = 1; i < sz; i++) {
LL dx = xs[i] - xs[i - 1], dy = ys[i] - ys[i - 1];
res += xs[i]*i - presumX;
res += ys[i]*i - presumY;
presumX += xs[i];
presumY += ys[i];
}
return res;
}
int main() {
scanf("%d%d", &n, &m);
unordered_map<int, vector<array<int, 2>>> mp;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
scanf("%d", &a);
mp[a].push_back({i, j});
}
}
LL ans = 0;
for(auto&[val, pos]: mp) {
ans += get(pos);
}
printf("%lld\n", ans);
return 0;
}