1. 程式人生 > 實用技巧 >單調佇列維護區間最值

單調佇列維護區間最值

https://blog.csdn.net/qq_41021816/article/details/81428225?utm_medium=distribute.pc_feed_404.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase&depth_1-utm_source=distribute.pc_feed_404.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecas

假設求一個序列中所有長度為k的區間的最值,我們在求1到k區間的最值後,2到k+1區間的最值可以用2到k區間的最值與第k+1個值進行比較,可以通過單調佇列維護2到k區間的最值

2020牛客多校第二場,單調佇列維護二維區間最值

#include<bits/stdc++.h>
#define ri register int
#define ll long long
#define EPS 1e-7
#define INF 0x7fffffff
#define lb(x) x&(-x)
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
const inline int read(){
    int k = 0, f = 1; char c = getchar();
    
for(;!isdigit(c); c = getchar()) if(c == '-') f = -1; for(;isdigit(c); c = getchar()) k = k * 10 + c - '0'; return k * f; } int n, m, k; const int maxn = 5005; int a[maxn][maxn], h[maxn][maxn]; int que[maxn]; ll ans = 0; int gcd(int x,int y){return x%y==0?y:gcd(y,x%y);} void solve(){
for(ri i = 1; i <= n; ++i) for(ri j = 1; j <= m; ++j) a[i][j] = i * j / gcd(i, j); for(ri i = 1; i <= n; ++i){ int head = 1, tail = 1; que[tail] = 0; for(ri j = 1; j < k; ++j){ while(tail >= head && a[i][j] > a[i][que[tail]]) tail--; que[++tail] = j; } for(ri j = k; j <= m; ++j){ while(tail >= head && j - que[head] >= k) head++; while(tail >= head && a[i][j] > a[i][que[tail]]) tail--; que[++tail] = j; h[i][j] = a[i][que[head]]; } } for(ri j = k; j <= m; ++j){ int head = 1, tail = 1; que[tail] = 0; for(ri i = 1; i < k; ++i){ while(tail >= head && h[i][j] > h[que[tail]][j]) tail--; que[++tail] = i; } for(ri i = k; i <= n; ++i){ while(tail >= head && i - que[head] >= k) head++; while(tail >= head && h[i][j] > h[que[tail]][j]) tail--; que[++tail] = i; ans += h[que[head]][j]; } } } int main(){ fast; cin >> n >> m >> k; solve(); cout << ans << "\n"; return 0; }
View Code