1. 程式人生 > 實用技巧 >2020牛客多校第二場 Fake Maxpooling

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/

感覺rmq好像也是可以求解的,先佔個坑,等寫了再過來修改。

單調佇列程式碼:

 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.front()]<=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(); //因為下邊是先插入i再記錄,所以一定要是<= 不然會多一個數,wa死了 30 while(q.size()&&a[i][q.front()]<=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.front()][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.front()][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 }