洛谷 P3572 [POI2014]PTA-Little Bird 题解 (单调队列优化DP)

题目链接

我们设 f [ i ] f[i] f[i] 表示小鸟从第 1 1 1 棵树跳到第 i i i 棵树的最小花费。

那么,朴素的 O ⁡ ( n 2 ) \operatorname{O}(n^2) O(n2) 转与方程就是 f [ i ] = f [ j ] + [ d [ j ] ≤ d [ i ] ] f[i]=f[j]+[d[j]\le d[i]] f[i]=f[j]+[d[j]d[i]],其中 [ d [ j ] ≤ d [ i ] ] [d[j]\le d[i]] [d[j]d[i]] 如果中括号中的条件满足则为 1 1 1,否则为 0 0 0

我们可以发现,对于 x < y x <y x<y

  • 如果 f [ x ] < f [ y ] f[x]<f[y] f[x]<f[y],那么 x x x 肯定没有用了。因为无论转移到哪里 y y y 都不比 x x x 差,而且 x x x 会先被淘汰掉
  • 同理,如果 f [ x ] = = f [ y ] f[x]==f[y] f[x]==f[y] 并且 d [ x ] ≤ d [ y ] d[x] \le d[y] d[x]d[y],那么 x x x 也肯定没用了

那么,我们就可以用单调队列维护,总时间复杂度 O ⁡ ( n ) \operatorname{O}(n) O(n)

#include<cstdio>
#include<iostream>
#include<deque>
#include<cstring>
using namespace std;
const int Maxn=1000000+10;
int a[Maxn],f[Maxn];
int n,m,k;
deque <int> q;
inline int read()
{
    
    
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
    
    if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return s*w;
}
void push(int pos)
{
    
    
	while(q.size())
	{
    
    
		int x=q.back();
		if(f[x]<f[pos] || f[x]==f[pos] && a[x]>a[pos])break;
		q.pop_back();
	}
	q.push_back(pos);
}
void pop(int i)
{
    
    
	while(q.size())
	{
    
    
		if(q.front()>=i-k)break;
		q.pop_front();
	}
}
int main()
{
    
    
//	freopen("in.txt","r",stdin);
	n=read();
	for(int i=1;i<=n;++i)
	a[i]=read();
	m=read();
	while(m--)
	{
    
    
		k=read();
		q.push_back(1);
		for(int i=2;i<=n;++i)
		{
    
    
			pop(i);
			int j=q.front();
			f[i]=f[j];
			if(a[j]<=a[i])++f[i];
			push(i);
		}
		printf("%d\n",f[n]);
		
		q.clear();
		memset(f,0,sizeof(f));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Brian_Pan_/article/details/107807988