牛客多校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<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
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++)
	{
		deque<Node>q;
		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().id<left)//将过期的值删掉(头部)
				q.pop_front();
			mmax[i][j]=q.front().val;
		}
	}
	LL ans=0;
	for(int j=k;j<=m;j++)
	{
		deque<Node>q;
		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<left)//将过期的值删掉(头部)
				q.pop_front();
			ans+=q.front().val;
		}
	}
	printf("%lld\n",ans);






    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/107329356