题目链接: F-Fake Maxpooling
Description
给出 n, m, k 求矩阵 a[n][m] 中 k方阵大小的区域内的最大值的和,a[i][j] = lcm(i, j)。
Sample Input
3 4 2
Sample Output
38
More Info
- 1 <= n, m <= 5000
- 1 <= k <= {n, m}
Method
- 可以暴力产生最小公倍数矩阵,可以花费时间换空间;
我就是补题的时候因为空间被卡的,蒟蒻哭泣 - 也可以用空间换时间,建一个gcd矩阵,然后找到lcm矩阵a;
- 得到a矩阵后进行两次单调队列即可,第一次将每一列的k区间的最值更新到a矩阵中,第二次则将每一行的k区间的最值更新,更新后的矩阵a[i][j]即为在1-i、1-j区间内 k方阵内的最大值;
- 最后将i-n、j-m的a[i][j]的值求和即可;
Code
详见注释
#include <iostream>
#include <cstdio>
#include <deque>
using namespace std;
#define ll long long
#define Max 5005
template<typename T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
int a[Max][Max], Gcd[Max][Max];
ll sum=0;
void solve(int &n, int &m, int &k)
{
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
if(!Gcd[i][j]) {
//a[i][j] = i*j/gcd(i, j);
for(int v=1; v*i<=n&&v*j<=m; v++) //快速求最小公倍数表
{
Gcd[v*i][v*j] = v;
a[v*i][v*j] = i*j*v;
}
}
for(int i=1; i<=n; i++) //单调队列 求纵向最值
{
deque<int> dq; //STL-双端队列
for(int j=1; j<=m; j++)
{
while(!dq.empty()&&a[i][j] > a[i][dq.back()]) dq.pop_back();
while(!dq.empty()&&j-dq.front() >= k) dq.pop_front();
dq.push_back(j);
a[i][j] = a[i][dq.front()]; //更新矩阵
}
}
for(int i=1; i<=m; i++) //单调队列 求横向最值
{
deque<int> dq; //STL-双端队列
for(int j=1; j<=n; j++)
{
while(!dq.empty()&&a[j][i] > a[dq.back()][i]) dq.pop_back();
while(!dq.empty()&&j-dq.front() >= k) dq.pop_front();
dq.push_back(j);
a[j][i] = a[dq.front()][i]; //更新矩阵
if (j >= k&& i >= k) sum += a[j][i]; //将所有k矩阵的最值求和
}
}
}
int main()
{
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
solve(n, m, k);
/*for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
printf("%lld ", a[i][j]);
printf("\n");
}*/
printf("%lld", sum);
return 0;
}
蒟蒻一只,欢迎指正