【题解】PTA-Little Bird

Link

单调队列板子。

题目大意:一个点可以由距离它不超过\(k\)的点跳过来,如果那个点比它高就不需要花费体力,否则花费\(1\)的体力。问走到\(n\)的最小体力,多组询问。

显然的转移方程,设计\(dp_i\)表示跳到\(i\)的最小体力,枚举\(i-k \to i-1\)转移即可。

这东西显然\(O(n)\),复杂度爆炸。

我们用单调队列优化一波,维护的是一个区间具有单调性的下标。

我们每次插入一个点到队尾,即序列右端,插入之前,看看尾部的数:如果这个数的\(dp\)值大于要插入的这个数或者是\(dp\)值相等但高度要小,则把它踢出去。

那么这样我们维护了一个从左到右单调递增的一个\(dp\)序列。每次更新找最左端那个下标对应\(dp\)值更新即可。

实现上使用的双端队列,这题比较卡常,需要\(O_2\).复杂度是\(O(qn).\)

#include<bits/stdc++.h>
using namespace std;

deque<int>q;
int n,Q,a[2000010];
int k,dp[2000010];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%d",a+i);
	scanf("%d",&Q);
	for(;Q;--Q){
		scanf("%d",&k);q.clear();
		q.push_back(1);
		for(int i=2;i<=n;++i){
			while(!q.empty()&&i-q.front()>k)q.pop_front();
			if(a[q.front()]>a[i])dp[i]=dp[q.front()];
			else dp[i]=dp[q.front()]+1;
			while(!q.empty()&&(dp[q.back()]>dp[i]||(dp[q.back()]==dp[i]&&a[q.back()]<=a[i])))q.pop_back();
			q.push_back(i);
		}
		printf("%d\n",dp[n]);
	}
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/h-lka/p/12708517.html
今日推荐