2020牛客暑期多校训练营(第二场) F-Fake Maxpooling
链接
题意
一个\(n*m\)的矩阵A,每个元素\(A_{i,j}\)的值是lcm(i, j),求所有\(k*k\)子矩阵中最大元素的和
思路
每行求出所有长度为k的区间中的最大值,再对求出的行最大值矩阵的每列进行同样的操作,最后求和。
至于区间最大值,可以用单调队列求解,但是不知道为啥队友t了,可能比赛用的机子波动吧
代码
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define ms(a) memset(a, 0, sizeof(a))
#define repu(i, a, b) for (int i = a; i < b; i++)
#define repd(i, a, b) for (int i = a; i > b; i--)
using namespace std;
typedef long long ll;
typedef long double ld;
const int M = int(1e3) * 5 + 5;
const int mod = int(1e9) + 7;
inline int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
int a[M][M];
int ma[M][M];
deque<int> q;
int main() {
int n, m, k;
cin >> n >> m >> k;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
a[i][j] = i * j / gcd(i, j);
}
}
for (int i = 1; i <= n; i++) {
while (!q.empty()) q.pop_back();
q.push_back(0);
for (int j = 1; j < k; j++) {
while (q.size() && a[i][q.front()] <= a[i][j]) {
q.pop_back();
}
q.push_back(j);
}
for (int j = k; j <= m; j++) {
while (q.size() && q.front() <= j - k) {
q.pop_front();
}
while (q.size() && a[i][q.back()] <= a[i][j]) {
q.pop_back();
}
q.push_back(j);
ma[i][j] = max(ma[i][j], a[i][q.front()]);
}
}
ll ans = 0;
for (int j = k; j <= m; j++) {
while (!q.empty()) {
q.pop_back();
}
q.push_back(0);
for (int i = 1; i < k; i++) {
while (q.size() && ma[q.front()][j] <= ma[i][j]) {
q.pop_back();
}
q.push_back(i);
}
for (int i = k; i <= n; i++) {
while (q.size() && q.front() <= i - k) {
q.pop_front();
}
while (q.size() && ma[q.front()][j] <= ma[i][j]) {
q.pop_back();
}
q.push_back(i);
ans += ma[q.front()][j];
}
}
cout << ans << endl;
return 0;
}
备注
据说用__gcd会t,建议家中常备递归式gcd
这种暴力求A的方法复杂度是\(O(nmlog_n)\)的,题解给了个\(O(nm)\)的解法,值得学习
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
if (!Gcd[i][j])
for (int k = 1; k * i <= n && k * j <= m; k ++)
Gcd[k * i][k * j] = k, A[k * i][k * j] = i * j * k;
类似一个筛,枚举所有的gcd
————————————————
心里有光,哪儿都美
心里有光,哪儿都美