F Fake Maxpooling(单调队列)

题目传送门

Fake Maxpooling

题目大意

对于一个 n ∗ m n*m nm的矩阵,每个位置的值为 A i j = l c m ( i , j ) A_{ij}=lcm(i,j) Aij=lcm(i,j)
求每个大小为 k ∗ k k*k kk的子矩阵的最大值的和

思路

直接暴力的话复杂度会很高,所以可以采取单调队列维护区间最大值

代码

#include
#include
#include
#include
using namespace std;
// #define TDS_ACM_LOCAL
const int N=5009;
int n, m ,k;
int a[N][N], b[N][N];
deque<int> q;
inline int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}

void solve(){
	memset(b, 0, sizeof(b));
	cin>>n>>m>>k;
	for(int i=1; i<=n; i++)
		for(int j=1; j<=m; j++)
			a[i][j]=i*j/gcd(i, j);

	for(int i=1; i<=n; i++){
		q.clear();				
		for(int j=1; j<=m; j++){
			while(!q.empty() && q.front()<=j-k)	q.pop_front();				//维护的是一个区间,我们只关注最大值的下标
			while(!q.empty() && a[i][q.back()]<=a[i][j])	q.pop_back();	//更新队列(新进入的值从队尾开始删除不大于它的值
			q.push_back(j);
			b[i][j]=max(b[i][j], a[i][q.front()]);			//将维护的区间的最大值存到b中
		}
	}
	long long ans=0;
	for(int i=k; i<=m; i++){
		q.clear();                    
		for(int j=1; j<=n; j++){
			while(!q.empty() && q.front()<=j-k)	q.pop_front();
			while(!q.empty() && b[q.back()][i]<= b[j][i])	q.pop_back();
			q.push_back(j);
			if(j>=k)	ans+=b[q.front()][i];			//注意前k-1列无法形成k*k的子矩阵的
		}
	}
	cout<<ans<<endl;
	return ;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
#ifdef TDS_ACM_LOCAL
    freopen("D:\\VS code\\.vscode\\testall\\in.txt", "r", stdin);
    freopen("D:\\VS code\\.vscode\\testall\\out.txt", "w", stdout);
#endif
    solve();
    return 0;
}

你可能感兴趣的:(单调队列)