2020牛客暑期多校训练营(第二场)Fake Maxpooling

传送门:Fake Maxpooling

题意:给出矩阵的行数n和列数m,矩阵 Aij = lcm( i , j )  ,求每个大小为k*k的子矩阵的最大值的和。

题解:如果暴力求解肯定会t,所以要智取。前几天刷蓝书的时候看到这种求区间最值的可以用单调队列,这个题就是用单调队列求解。先横着算一下每个长度为k的区间的最大值记录下来,然后再把记录下来的数组竖着同样算一下,最后求和。求最小公倍数的时候,不能用__gcd(),会 t 的。这个题的矩阵不会超long long ,所以用 int 就好了,不然会超内存。(血泪史

至于单调队列是什么,可以看看这个,在acwing写题的时候看到的。https://www.acwing.com/blog/content/150/

代码:

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 int a[5100][5100],b[5100][5100];
 5 deque<int>q;
 6 inline int gcd(int a,int b){
 7     return b==0?a:gcd(b,a%b);
 8 }
 9 int main()
10 {
11     ios::sync_with_stdio(false);
12     cin.tie(0);
13     cout.tie(0);
14     int n,m,k;
15     cin>>n>>m>>k;
16     for(int i=1;i<=n;i++){
17         for(int j=1;j<=m;j++){
18             a[i][j]=i*j/gcd(i,j);
19         }
20     }
21     for(int i=1;i<=n;i++){
22         while(!q.empty()) q.pop_back();
23         q.push_back(0);
24         for(int j=1;j<k;j++){
25             while(q.size()&&a[i][q.back()]<=a[i][j]) q.pop_back();
26             q.push_back(j);
27         }
28         for(int j=k;j<=m;j++){
29             while(q.size()&&q.front()<=j-k) q.pop_front();    //因为下边是先插入j再记录,所以一定要是<= 不然会多一个数,wa死了
30             while(q.size()&&a[i][q.back()]<=a[i][j]) q.pop_back();
31             q.push_back(j);
32             b[i][j]=max(b[i][j],a[i][q.front()]);
33         }
34     }
35     ll ans=0;
36     for(int j=k;j<=m;j++){
37         while(!q.empty()) q.pop_back();
38         q.push_back(0);
39         for(int i=1;i<k;i++){
40             while(q.size()&&b[q.back()][j]<=b[i][j]) q.pop_back();
41             q.push_back(i);
42         }
43         for(int i=k;i<=n;i++){
44             while(q.size()&&q.front()<=i-k) q.pop_front();
45             while(q.size()&&b[q.back()][j]<=b[i][j]) q.pop_back();
46             q.push_back(i);
47             ans+=b[q.front()][j];
48         }
49     }
50     cout<<ans<<endl;
51     return 0;
52 }

 

posted @ 2020-07-13 19:49  只能过样例嘤嘤嘤  阅读(253)  评论(0编辑  收藏  举报

……