单调队列小专题

  1. 概念题

滑动窗口
模板题,维护一个值和一个下标即可

Luogu2216
首先对每行做一遍滑动窗口,然后发现其实列也是个滑动窗口

// by SkyRainWind
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;
#define int LL

const int inf = 1e9, INF = 0x3f3f3f3f,maxn=1005;

int n,m,k,a[maxn][maxn];
int mx[maxn][maxn], mx2[maxn][maxn];
int mn[maxn][maxn], mn2[maxn][maxn];

namespace s1{
	int qu[maxn],qu2[maxn],qu3[maxn],qu4[maxn];
	void solve(){
		for(int i=1;i<=n;i++){
			int hd=1, tl=0;
			for(int j=1;j<=m;j++){
				while(hd <= tl && qu[tl] <= a[i][j])-- tl;
				qu[++ tl] = a[i][j];
				qu2[tl] = j;
				if(j < k)continue;
				while(hd <= tl && j - qu2[hd] >= k) ++ hd;
				mx[i][j - k + 1] = qu[hd];
			}
		}
		for(int j=1;j<=m-k+1;j++){
			int hd=1, tl=0;
			for(int i=1;i<=n;i++){
				while(hd <= tl && qu3[tl] <= mx[i][j]) -- tl;
				qu3[++ tl] = mx[i][j];
				qu4[tl] = i;
				if(i < k)continue;
				while(hd <= tl && i - qu4[hd] >= k) ++ hd;
				mx2[i - k + 1][j] = qu3[hd];
			}
		}
	}
};

namespace s2{
	int qu[maxn],qu2[maxn],qu3[maxn],qu4[maxn];
	void solve(){
		for(int i=1;i<=n;i++){
			int hd=1, tl=0;
			for(int j=1;j<=m;j++){
				while(hd <= tl && qu[tl] >= a[i][j])-- tl;
				qu[++ tl] = a[i][j];
				qu2[tl] = j;
				if(j < k)continue;
				while(hd <= tl && j - qu2[hd] >= k) ++ hd;
				mn[i][j - k + 1] = qu[hd];
			}
		}
		for(int j=1;j<=m-k+1;j++){
			int hd=1, tl=0;
			for(int i=1;i<=n;i++){
				while(hd <= tl && qu3[tl] >= mn[i][j]) -- tl;
				qu3[++ tl] = mn[i][j];
				qu4[tl] = i;
				if(i < k)continue;
				while(hd <= tl && i - qu4[hd] >= k) ++ hd;
				mn2[i - k + 1][j] = qu3[hd];
			}
		}
	}
};

signed main(){
	scanf("%lld%lld%lld",&n,&m,&k);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)scanf("%lld",&a[i][j]);
	s1::solve();
	s2::solve();
	LL ans = 1e18;
	for(int i=1;i<=n-k+1;i++)
		for(int j=1;j<=m-k+1;j++)
			ans = min(ans, mx2[i][j] - mn2[i][j]);
	printf("%lld\n",ans);

	return 0;
}
  1. 单调队列优化dp
    常见形式是:\(dp[i] = max{dp[j]} + w\),其中j在某个与i有关的范围内
    用单调队列维护dp数组

Luogu2034
\(j \in [i-k-1, i-1]\)

// by SkyRainWind
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 2e5+ 5;

int n,k;
int a[maxn];
LL dp[maxn], qu[maxn], qu2[maxn];

signed main(){
	scanf("%d%d",&n,&k);
	LL sum = 0;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]), sum += a[i];
	int hd=1, tl=0;
//	for(int i=1;i<=k;i++)dp[i] = 0, qu[++ tl] = 0, qu2[tl] = i;
	qu[++ tl] = 0;qu2[tl] = 0;
	for(int i=1;i<=n;i++){
		while(hd <= tl && qu[tl] >= qu[hd] + a[i])-- tl;
		dp[i] = qu[hd] + a[i];	// i-k-1 .. i-1
		qu[++ tl] = dp[i];
		qu2[tl] = i;
		while(hd <= tl && i - qu2[hd] >= k+1)++ hd;
	}
//	printf("%lld\n",dp[3]);
	LL r = 1e18;
	for(int i=n-k;i<=n;i++)r = min(r, dp[i]);
	printf("%lld\n",sum - r);

	return 0;
}

Luogu3957
形式相同,只不过\(j \in [i-mx, i-mn]\)
可以用一个\(O(n log^2n)\)+剪枝的暴力AC
考虑单调队列做法,其实差不多,只不过入队和出队的时候需要判断一下,注意顺序matters!

// by SkyRainWind
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;
//#define int LL

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 5e5 + 5;

int n,d,k;
int x[maxn], s[maxn];
LL dp[maxn];
LL qu[maxn], qu3[maxn];
int qu2[maxn], qu4[maxn];

int check(int lim){
	int mn = max(d-lim, 1), mx = d + lim;
	
	int hd=1, tl=0;
	for(int i=1;i<=n;i++)qu[i] = qu2[i] = 0;
	int l=0, r=0;
	for(int i=1;i<=n;i++){
		while(x[i] - x[r] >= mn){
			while(hd <= tl && qu[tl] <= dp[r])-- tl;
			qu[++ tl] = dp[r];qu2[tl] = r;
			++ r;
		}
		while(x[i] - x[l] > mx){	// 放在后面以排除 距离 > mx 且 > mn的不合法情况 
			if(qu[hd] == dp[l] && qu2[hd] == l)++ hd;
			++ l;
		}
		if(hd > tl)dp[i] = -1e18;
		else dp[i] = qu[hd] + s[i];
		if(dp[i] >= k)return 1;
//		printf("%d %d %d %d\n",i,dp[i], hd,tl);
	}
	return 0;
}

signed main(){
//	freopen("3957_6.in","r",stdin);
	scanf("%d%d%d",&n,&d,&k);
	LL r1 = 0;
	for(int i=1;i<=n;i++)scanf("%d%d",&x[i],&s[i]), r1 += s[i] >= 0 ? s[i] : 0;
	if(r1 < k){
		puts("-1");
		return 0;
	}
//	printf("%d\n",check(235));return 0;
	int l=1, r=1e9, ans;
	while(l <= r){
		int mid = l+r>>1;
		if(check(mid))r = mid-1, ans = mid;
		else l = mid+1;
	}
	printf("%d\n",ans);

	return 0;
}
posted @ 2022-10-07 21:30  SkyRainWind  阅读(14)  评论(0编辑  收藏  举报