2020牛客多校第二场F题Fake Maxpooling(单调队列维护区间最值)
题意:给你n*m的最小公倍数矩阵,求每个k*k方阵里的的最大元素值的和
题解:单调队列维护区间最值,先从左到右,记录最大值到b数组
再从上到下,记录最大值,ans+=最大值
最小公倍数矩阵暴力枚举O(n*m*logn) ,优化方法为O(nm),但是空间会多一倍,会被卡空间...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #include <bits/stdc++.h> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=5e3+7; const ll mod =1e9+7; int ma[maxn][maxn],b[maxn][maxn]; //int Gcd[maxn][maxn]; int main(){ IOS int n,m,k; cin>>n>>m>>k; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ ma[i][j]=i/__gcd(i,j)*j; } } // for (int i = 1; i <= n; i ++) // for (int j = 1; j <= m; j ++) // if (!Gcd[i][j]) // for (int k = 1; k * i <= n && k * j <= m; k ++) // Gcd[k * i][k * j] = k, ma[k * i][k * j] = i * j * k; int maxx=0; ll ans=0; deque<int> q; for(int i=1;i<=n;i++){ while(!q.empty()) q.pop_back(); q.push_back(0); for(int j=1;j<k;j++){ while(q.size()&&ma[i][q.back()] <=ma[i][j] ) q.pop_back(); q.push_back(j); } for(int j=k;j<=m;j++){ while(q.size() && q.front()<=j-k ) q.pop_front(); while(q.size() && ma[i][q.back()] <= ma[i][j]) q.pop_back(); q.push_back(j); b[i][j]=max(b[i][j],ma[i][q.front()]); } } // for(int i=1;i<=n;i++){ // for(int j=1;j<=m;j++){ // cout<<b[i][j]<<" "; // } // cout<<endl; // } for(int j=k;j<=m;j++){ while(!q.empty()) q.pop_back(); q.push_back(0); for(int i=1;i<k;i++){ while(q.size()&&b[q.back()][j]<=b[i][j]) q.pop_back(); q.push_back(i); } for(int i=k;i<=n;i++){ while(q.size()&&q.front()<=i-k) q.pop_front(); while(q.size()&&b[q.back()][j]<=b[i][j]) q.pop_back(); q.push_back(i); ans+=b[q.front()][j]; } } cout<<ans<<endl; return 0; }