【2020年牛客暑假第二场】F题Fake Maxpooling

【2020年牛客暑假第二场】F题Fake Maxpooling

题目连接:https://ac.nowcoder.com/acm/contest/5667/Fhtml

题目描述
Given a matrix of size n × m n×m and an integer k k ,where A i × j = l c m ( i , j ) A_{i\times j} = lcm(i,j) the least common multiple of i i and j j . You should determine the sum of the maximums among all k × k k×k submatrices.java

输入描述
Only one line containing three integers n , m , k ( 1 n , m 5000 , 1 k m i n { i , j } ) n,m,k(1\leq n,m\leq 5000,1\leq k \leq min\begin{Bmatrix}i,j\end{Bmatrix}) ios

输出描述
Only one line containing one integer, denoting the answer.web

输入
3 3 4 4 2 2 数组

输出
38 38 app

思路

队友用的是二维ST表AC的,这里是单调队列。svg

矩阵的全部lcm能够在单调过程当中记忆化,复杂度会减小,若是先作预处理(单调前所有计算出会加大复杂度)。spa

只须要纵向作一遍单调队列,而后把纵向全部的单调队列存在Max二维数组里, 以后在横向作一遍单调队列,这样k*k的方格就能找到最大值了。code

Code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair<double, double> pdd;

#define INF 0x7f7f7f
#define mem(a,b) memset(a , b , sizeof(a))
#define FOR(i, x, n) for(int i = x;i <= n; i++)
// const ll mod = 1e9 + 7;
// const int maxn = 1e5 + 10;
// const double eps = 1e-6;

int gcd(int a, int b)
{
    return b == 0 ? a : gcd(b, a % b);
}

struct Monotone_queue
{
    static const int maxn = 5001;
    int n, m, k; // 个数、窗口大小
    int q[maxn], p[maxn], head, tail; // q是单调队列,p是对应y序号,头节点、尾节点
    int Max[maxn][maxn];
    void read()
    {
        cin >> n >> m >> k;
    }
    void Monotone()
    {
        for(int i = 1;i <= n; i++)
        {
            head = 1; tail = 0;
            for(int j = 1;j <= m; j++)
            {
                int val = i * j / gcd(i, j);
                while(head <= tail && q[tail] <= val)
                    tail--;
                q[++tail] = val;
                p[tail] = j;
                while(head <= tail && p[head] <= j - k)
                    head++;
                if(j >= k)
                    Max[i][j - k + 1] = q[head];
            }
        }
    }
    
    ll ans = 0;
    
    void value()
    {
        for(int j = 1; j<= m - k + 1; j++)
        {
            head = 1; tail = 0;
            for(int i = 1;i <= n; i++)
            {
                int val = Max[i][j];
                while(head <= tail && q[tail] <= val)
                    tail--;
                q[++tail] = val;
                p[tail] = i;
                while(head <= tail && p[head] <= i - k)
                    head++;
                if(i >= k)
                    ans += q[head];
            }
        }
        cout << ans << endl;
    }
}Worker;

void solve()
{
    Worker.read();
    Worker.Monotone();
    Worker.value();
}

signed main()
{
    ios_base::sync_with_stdio(false);
    //cin.tie(nullptr);
    //cout.tie(nullptr);
#ifdef FZT_ACM_LOCAL
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#else
    ios::sync_with_stdio(false);
    int T = 1;
    //cin >> T;
    while(T--)
        solve();
#endif
    return 0;
}

deque模拟单调队列

// 双向队列模拟

struct num{
    int index, x;
};

deque<num> q; // max
deque<num> p; // min

int s[2][10000005];
int cnt = 1;

void solve()
{
    int n, k, x;
    num t;
    cin >> n >> k;
    for(int i = 1;i <= n; i++)
    {
        cin >> x;
        t.index = i; t.x = x;
        while(!q.empty() && x >= q.back().x)
        {
            q.pop_back();
        }
        while(!p.empty() && x <= p.back().x)
        {
            p.pop_back();
        }
        q.push_back(t);
        p.push_back(t);
        while(i - k >= q.front().index)
        {
            q.pop_front();
        }
        while(i - k >= p.front().index)
        {
            p.pop_front();
        }
        if(i >= k)
        {
            s[0][cnt] = q.front().x;
            s[1][cnt] = p.front().x;
            cnt++;
        }
    }
    for(int i = 1;i < cnt; i++)
        cout << s[1][i] << " ";
    cout << endl;
    for(int i = 1;i < cnt; i++)
        cout << s[0][i] << " ";
    cout << endl;
}

数组模拟单调队列

// 数组模拟

struct Monotone_queue{
    static const int maxn = 10000005;
    int n, k, a[maxn]; // 个数、窗口大小、值
    int q[maxn], p[maxn], head, tail; // q是单调队列,p是对应y序号,头节点、尾节点
    
    void read()
    {
        cin >> n >> k;
        for(int i = 1;i <= n; i++)
            cin >> a[i];
    }
    
    void Monotone_min()
    {
        head = 1; tail = 0;
        for(int i = 1;i <= n; i++)
        {
            while(head <= tail && q[tail] >= a[i])
            {
                tail--;
            }
            q[++tail] = a[i];
            p[tail] = i;
            while(p[head] <= i - k)
                head++;
            if(i >= k)
                cout << q[head] << " ";
        }
        cout << endl;
    }
    
    void Monotone_max()
    {
        head = 1; tail = 0;
        for(int i = 1;i <= n; i++)
        {
            while(head <= tail && q[tail] <= a[i])
            {
                tail--;
            }
            q[++tail] = a[i];
            p[tail] = i;
            while(p[head] <= i - k)
                head++;
            if(i >= k)
                cout << q[head] << " ";
        }
        cout << endl;
    }
}Worker;