牛客多校2 - Fake Maxpooling(线性递推gcd+单调队列)

题目链接:点击查看

题目大意:给出一个矩阵 A 的大小,规定其元素 A[ i ][ j ] = lcm( i , j ) ,再给出一个 k ,求所有大小为 k * k 的子矩阵中的最大值之和

题目分析:题目时限给了三秒,可以直接 n * m * logn 去求出矩阵 A ,但题解提供了一种可以线性求解 gcd 的方法,所以可以优化掉一层 log,在求出矩阵 A 后,可以对于每一行,利用单调队列维护区间最大值,mmax[ i ][ j ] 记录 A[ i ][ j - k ] : A[ i ][ j ] 的最大值,最后再利用单调队列,求解一下 mmax[ i - k ][ j ] : mmax[ i ][ j ] 的最大值就是子矩阵 ( i - k , j - k ) ~ ( i , j ) 的最大值了,区间维护最大值的代码可以参考经典例题:滑动窗口

另外这个题目有点卡内存,如果是 5000 * 5000 的数组的话,最多只能开两个,所以可以将 A 数组与 mmax 数组进行一个复用

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

typedef long long LL;

typedef unsigned long long ull;

const int inf=0x3f3f3f3f;

const int N=5e3+100;

int g[N][N];

int mmax[N][N];//mmax[i][j]:max(maze[i][j-k]:maze[i][j])

struct Node
{
	int val,id;
	Node(int val,int id):val(val),id(id){}
};

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("input.txt","r",stdin);
//  freopen("output.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	int n,m,k;
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(!g[i][j])
				for(int k=1;k*i<=n&&k*j<=m;k++)
					g[k*i][k*j]=k,mmax[k*i][k*j]=i*j*k;
	for(int i=1;i<=n;i++)
	{
		dequeq;
		for(int j=1;j<=k-1;j++)
		{
			while(q.size()&&q.back().val<=mmax[i][j])//将比当前值小的数全删掉(尾部)
				q.pop_back();
			q.push_back(Node(mmax[i][j],j));
		}
		for(int j=k;j<=m;j++)
		{
			int left=j-k+1;
			while(q.size()&&q.back().val<=mmax[i][j])//将比当前值小的数全删掉(尾部)
				q.pop_back();
			q.push_back(Node(mmax[i][j],j));
			while(q.size()&&q.front().idq;
		for(int i=1;i<=k-1;i++)
		{
			while(q.size()&&q.back().val<=mmax[i][j])//将比当前值小的数全删掉(尾部)
				q.pop_back();
			q.push_back(Node(mmax[i][j],i));
		}
		for(int i=k;i<=n;i++)
		{
			int left=i-k+1;
			while(q.size()&&q.back().val<=mmax[i][j])//将比当前值小的数全删掉(尾部)
				q.pop_back();
			q.push_back(Node(mmax[i][j],i));
			while(q.size()&&q.front().id

 

你可能感兴趣的:(单调栈/单调队列,数论)