题目链接:https://ac.nowcoder.com/acm/contest/5667/F
题目描述
Given a matrix of size n × m n×m n×mand an integer k k k,where A i × j = l c m ( i , j ) A_{i\times j} = lcm(i,j) Ai×j=lcm(i,j)the least common multiple of i i i and j j j. You should determine the sum of the maximums among all k × k k×k k×k submatrices.
输入描述
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}) n,m,k(1≤n,m≤5000,1≤k≤min{i,j})
输出描述
Only one line containing one integer, denoting the answer.
输入
3 3 3 4 4 4 2 2 2
输出
38 38 38
队友用的是二维ST表AC的,这里是单调队列。
矩阵的所有lcm可以在单调过程中记忆化,复杂度会减少,如果先做预处理(单调前全部计算出会加大复杂度)。
只需要纵向做一遍单调队列,然后把纵向所有的单调队列存在Max二维数组里, 之后在横向做一遍单调队列,这样k*k的方格就能找到最大值了。
#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;
}
// 双向队列模拟
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;