传送门:题目
题解:
#include<iostream>
#include<string>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
int num[5005][5005];
struct n1{
int v;
int index;
}node[5005];
int gcd(int a, int b)
{
if(b == 0) return a;
else return gcd(b, a%b);
}
int main()
{
int n, m, k;
cin >> n >> m >> k;
for(int i = 1; i <= n; i++)
for(int z = 1; z <= m; z++)
{
num[i][z] = i/gcd(i, z)*z;
}
int head = 0;
int tail = 0;
for(int i = 1; i <= n; i++)
{
head = 0;
tail = 0;
for(int z = 1; z <= m; z++ )
{
if(head == tail)
{
tail++;
node[tail].v = num[i][z];
node[tail].index = z;
num[i][z] = node[head+1].v;
continue;
}
if(head < tail && node[head+1].index < z-k+1)
head++;
while(num[i][z] >= node[tail].v && head < tail)
{
tail--;
}
tail++;
node[tail].v = num[i][z];
node[tail].index = z;
if(z >= k)
num[i][z] = node[head+1].v;
}
}
head = 0;
tail = 0;
for(int z = k; z <= m; z++)
{
head = 0;
tail = 0;
for(int i = 1; i <= n; i++)
{
if(tail == head)
{
tail++;
node[tail].v = num[i][z];
node[tail].index = i;
num[i][z] = node[head+1].v;
continue;
}
if(head < tail && node[head+1].index < i-k+1)
head++;
while(num[i][z] >= node[tail].v && head < tail)
{
tail--;
}
tail++;
node[tail].v = num[i][z];
node[tail].index = i;
if(i >= k)
num[i][z] = node[head+1].v;
}
}
ll ans = 0;
for(int i = k; i <= n; i++)
{
for(int z = k; z <= m; z++)
{
ans += num[i][z];
}
}
cout << ans;
return 0;
}
这道题相当于是一个二维的滑动窗口,然后这里用到了单调队列,从大到小排列的单调队列里面的存一个结构体,一个是存那个节点的本身的值(v),另一个存这个节点的下标(index),首先我们先对行进行找最大值,那么这个队列里面的数量是一定小于K,然后把超出k范围的老节点从队头挤出去,然后用把新的节点加入其中,最大值就是对头。
然后再纵向的找,最后把ans相加。