单调队列解析

单调队列的定义

单调队列是一种特殊的队列数据结构,其元素保持严格的单调递增或单调递减顺序。与普通队列不同,单调队列在插入新元素时会移除破坏单调性的旧元素,确保队列始终有序。

核心特性

单调性维护:队列内的元素按单调递增或单调递减排列。
动态调整:插入新元素时,需从队列尾部或头部移除不符合单调性的元素。
高效操作:支持在均摊 (O(1)) 时间复杂度内完成插入、删除和查询操作。

典型应用场景

滑动窗口最大值/最小值:luogu P1886,通过单调队列在 (O(n)) 时间内解决。
优化动态规划:减少状态转移的时间复杂度,例如多重背包问题。

模板外简单题就直接放代码了 \color{red} {模板外简单题就直接放代码了} 模板外简单题就直接放代码了

滑动窗口

我们需要维护一个队列,这个队列是具有单调性的。
维护这个队列每加入一个元素有三步:

如果是单调递减队列:
排出所有比目前元素更的大的。
然后加入该元素。
排出所有过时元素。

如果是单调递增队列:
排出所有比目前元素更的小的。
然后加入该元素。
排出所有过时元素。

完整代码:

#include<bits/stdc++.h>
using namespace std;
int n,k,q[1000005],a[1000005];
void minn()
{
	int l = 1,r = 0;
	for(int i = 1;i <= n;i ++)
	{
		while(l <= r && a[q[r]] >= a[i] )
		{
			r --;
		}
		q[++ r] = i;
		while(l <= r && q[l] <= i - k)
		{
			l ++;
		}
		if(i >= k)cout << a[q[l]] << ' ';
	}
}
void maxn()
{
	int l = 1,r = 0;
	for(int i = 1;i <= n;i ++)
	{
		while(l <= r && a[q[r]] <= a[i] )
		{
			r --;
		}
		q[++ r] = i;
		while(l <= r && q[l] <= i - k)
		{
			l ++;
		}
		if(i >= k)cout << a[q[l]] << ' ';
	}
}
int main()
{
	cin >> n >> k;
	for(int i = 1;i <= n;i ++)
	{
		cin >> a[i];
	}
	minn();
	cout << '\n';
	maxn();
	return 0;
}

P9290 [ROI 2018] Decryption

#include<bits/stdc++.h>
using namespace std;
int stk[500005],maxn[500005],minn[500005],a[500005],n,top,l = 1,r = 1,cnt;
int main()
{
	cin >> n;
	for(int i = 1;i <= n;i ++)
	{
		cin >> a[i];
	}
	a[n + 1] = -1;
	for(int i = 1;i <= n + 1;i ++)
	{
		while(top > 0 && a[i] < a[stk[top]])
		{
			minn[stk[top]] = i;
			top --;
		}
		stk[++ top] = i;
	}
	top = 0;
	a[n + 1] = INT_MAX;
	for(int i = 1;i <= n + 1;i ++)
	{
		while(top > 0 && a[i] >= a[stk[top]])
		{
			maxn[stk[top]] = i;
			top --;
		}
		stk[++ top] = i;
	}
	while(l <= n)
	{
		while(maxn[r] < minn[l])
		{
			r = maxn[r];
		}
		l = r + 1;
		cnt ++;
		r = l;
	}
	cout << cnt;
	return 0;
}

P10334 [UESTCPC 2024] 饮料

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int M = 2e5 + 5;
int n,stk[M],t[M],a[M],top,ans;
signed main()
{
	cin >> n;
	for(int i = 1;i <= n;i ++)
	{
		cin >> t[i];
		if(i > t[i])
		{
			cout << -1;
			return 0;
		}
	}
	for(int i = 1;i <= n;i ++)
	{
		cin >> a[i];
	}
	stk[++top] = a[n];
	ans += a[n];
	for(int i = n - 1;i >= 0;i --)
	{
		if(t[i] == t[i + 1])
		{
			if(stk[top] >= a[i])
			{
				int x = stk[top];
				stk[++top] = x;
				ans += x;
			}
			else
			{
				stk[++top] = a[i];
				ans += a[i];
			}
		}
		else
		{
			for(int j = 1;j <= t[i + 1] - t[i];j ++)
			{
				if(top > 0)
				{
					top --;
				}
				else
				{
					break;
				}
			}
			if(top > 0 && stk[top] >= a[i])
			{
				int x = stk[top];
				stk[++top] = x;
				ans += x;
			}
			else
			{
				stk[++top] = a[i];
				ans += a[i];
			}
		}
	}
	cout << ans;
	return 0;
}

B3667 求区间所有后缀最大值的位置

#include<bits/stdc++.h>
using namespace std;
int n,k,q[1000005];
unsigned long long a[1000005];
void maxn()
{
	int l = 1,r = 0;
	for(int i = 1;i <= n;i ++)
	{
		while(l <= r && a[i] >= a[q[r]])
		{
			r --;
		}
		q[++r] = i;
		while(l <= r && q[l] <= i - k)
		{
			l ++;
		}
		if(i >= k)cout << r - l + 1 << '\n';
	}
}
int main()
{
	cin >> n >> k;
	for(int i = 1;i <= n;i ++)
	{
		cin >> a[i];
	}
	maxn();
	return 0;
} 

P8661 [蓝桥杯 2018 省 B] 日志统计

#include <bits/stdc++.h>
using namespace std;
int n,d,k;
int t = 1 , l , r , q[100050];
struct node
{
	int ts,id;
}a[100050];
bool cmp(node x,node y)
{
	if(x.id != y.id)return x.id < y.id;
	return x.ts < y.ts;
}
int main()
{
	cin >> n >> d >> k;
	for(int i = 1 ; i <= n ; i ++)
	{
		cin >> a[i].ts >> a[i].id;
	}
	sort(a + 1 , a + n + 1 , cmp);
	for(int i = 1 ; i <= n ; i ++)
	{
		while(a[t].id == a[i].id)i ++;
		i --;
		l = 1;
		r = 0;
		for(int j = t ; j <= i ; j ++)
		{
			while( l <= r && a[q[l]].ts + d <= a[j].ts ) l ++;
			q[ ++ r ] = j;
			if( r - l + 1 >= k )
			{
				cout << a[t].id << '\n' ;
				t = i + 1;
				break;
			}
			if(j == i) t = i + 1;
		}
	}
	return 0;
}

P1440 求m区间内的最小值

#include<bits/stdc++.h>
using namespace std;
deque<int>qn;
vector<int>minn;
int n,m,a[2000005];
int main()
{
	scanf("%d %d",&n,&m);
	for(int i = 1;i <= n;i ++)
	{
		scanf("%d",&a[i]);
		if(!qn.empty() && qn.front() == i - m - 1)
		{
			qn.pop_front();
		}
		while(!qn.empty() && a[qn.back()] >= a[i - 1])
		{
			qn.pop_back();
		}
		if(i != 1)qn.push_back(i - 1);
		if(!qn.empty())minn.push_back(a[qn.front()]);
		else minn.push_back(0);
	}
	for(auto it:minn)printf("%d\n",it);
	return 0;
}

P3512 [POI 2010] PIL-Pilots

#include<bits/stdc++.h>
using namespace std;
const int M = 3e6 + 5;
int n,k,qn[M],qx[M],a[M],ln = 1,lx = 1,rn,rx,pos = 1,ans;
int main()
{
	cin >> k >> n;
	for(int i = 1;i <= n;i ++)
	{
		cin >> a[i];
	}
	for(int i = 1;i <= n;i ++)
	{
		while(ln <= rn && a[qn[rn]] >= a[i])
		{
			--rn;
		}
		while(lx <= rx && a[qx[rx]] <= a[i])
		{
			--rx;
		}
		qn[++rn] = i;
		qx[++rx] = i;
		while(ln <= rn && lx <= rx && (a[qx[lx]] - a[qn[ln]]) > k)
		{
			if(qx[lx] < qn[ln])
			{
				pos = qx[lx] + 1;
				lx ++;
			}
			else
			{
				pos = qn[ln] + 1;
				ln ++;
			}
		}
		ans = max(ans,i - pos + 1);
	}
	cout << ans;
	return 0;
} 

P10059 Choose

#include<bits/stdc++.h>
using namespace std;
const int M = 1e5 + 5;
int t,n,k,a[M],qn[M],qx[M],ln = 1,lx = 1,mxn[M],mnn[M],ans = 2e9,rn,rx;
bool flag = false;
int check(int len)
{
	int cnt = 0;
	ln = 1,lx = 1,rn = 0,rx = 0;
	for(int i = 1;i <= n;i ++)
	{
		while(ln <= rn && a[qn[rn]] >= a[i]) // Min 
		{
			-- rn;
		}
		while(lx <= rx && a[qx[rx]] <= a[i]) // Max 
		{
			-- rx;
		}
		qn[++ rn] = i;
		qx[++ rx] = i;
		while(ln <= rn && qn[ln] <= i - len)
		{
			++ ln;
		}
		while(lx <= rx && qx[lx] <= i - len)
		{
			++ lx;
		}
		if(i >= len)
		{
			if(!flag)
			{
				ans = min(a[qx[lx]] - a[qn[ln]] , ans);
			}
			else
			{
				if(a[qx[lx]] - a[qn[ln]] >= ans)
				{
					cnt ++;
				}
			}
		}
	}
	flag = true;
	return cnt;
} 
int main()
{
	cin >> t;
	while(t --)
	{
		ans = 2e9;
		flag = false; 	
		cin >> n >> k;
		for(int i = 1;i <= n;i ++)cin >> a[i];
		check(n - k + 1);
		int l = 0,r = n - k + 2;
		while(l + 1 < r)
		{
			int mid = (l + r) / 2;
			if(check(mid) >= k)
			{
				r = mid;
			}
			else
			{
				l = mid;
			}
		}
		cout << ans << ' ' << r << '\n';
	}
	return 0;
} 

P2032 扫描

#include<bits/stdc++.h>
using namespace std;
struct node
{
	int pos,it;
};
deque < node > q;
int n,k,a[2000005];
int main(){
	cin >> n >> k;
	for(int i = 1;i <= n;i ++)cin >> a[i];
	for(int i = 1;i <= n;i ++)
	{
		if( !q.empty() && q.front().pos <= i - k )
		{
			q.pop_front();
		}
		while( !q.empty() && q.back().it <= a[i] )
		{
			q.pop_back();
		}
		q.push_back( { i , a[i] } );
		if(i >= k)cout << q.front().it << '\n';
	}
	return 0;
}

P3029 [USACO11NOV] Cow Lineup S

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int x,id;
}s[50005];
int ans = INT_MAX,sum,n,z,rx;
map<int,int>t;
map<int,bool>pan;
bool cmp(node a,node b)
{
    return a.x < b.x;
}
int main() {
    cin >> n;
    for(int i = 1 ; i <= n ; i ++)
	{
    	cin >> s[i].x >> s[i].id;
    	if(pan[ s[ i ].id ] == false)
		{
    		sum ++;
    		pan[ s[ i ].id ] = true;
		}
	}
    sort(s + 1 , s + n + 1 , cmp);
    rx = 1;
    t[ s[ 1 ].id ] ++;
    z=1;
    for(int i = 1 ; i <= n ; i ++)
	{
        while(z < sum && rx < n)
		{
            rx ++;
            t[ s[ rx ].id ] ++;
            if( t[ s[ rx ].id ] == 1 ) z ++;
        }
        if(z == sum)
		{
			ans = min( ans , s[rx].x - s[i].x );
		}
        t[ s[ i ].id ] --;
        if( t[ s[ i ].id ] == 0 )
		{
			z --;
		}
    }
    cout << ans;
    return 0;
}

P3069 Cow Lineup G

#include <bits/stdc++.h>
#define c(x) scanf("%d",&x)
#define p(x) printf("%d\n",x)
using namespace std;
const int N = 1e5 + 5;
map < int , int > g;
int n,k;
int a[N];

int main()
{
	c(n);
	c(k);
	for(int i = 1 ; i <= n ; i ++) 
	{
		c(a[i]);
	}
	int l = 1 , r = 0 , cnt = 0 , ans = 0;
	while(r <= n)
	{
		if(++ g[ a[ ++ r ] ] == 1) cnt ++;
		while(cnt == k + 2)
		{
			if( !( -- g[ a[ l ++ ] ] ) )
			{
				cnt --;
			}
		}
		ans = max( ans , g[ a[ r ] ] );
	}
	p(ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值