2020牛客暑期多校训练营(第二场) F-Fake Maxpooling

链接

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

原文地址:https://www.cnblogs.com/harutomimori/p/13327005.html