P3572 [POI2014] PTA-Little Bird

思路:

考虑动态规划算法,定义 \(dp_i\) 表示从 \(i\)\(n\) 的最小疲劳值。

\[dp_i = \min\limits_{j=i+1}^{i+k} dp_j + [a_i \le a_j] \]

此时时间复杂度为 \(O(qn^2)\),可以拿到 50pts 的好成绩,需要优化。

考虑维护一个 \(dp_j\) 单调上升的单调队列,同时为了使得 \(a_i \le a_j\) 尽量不被满足,则当两位置的 \(dp\) 值相同时,则保留 \(a_j\) 较小的那个不被弹出。

此时时间复杂度为 \(O(qn)\)

有一句经典的话:如果一个选手比你小,还比你强,你就可以退役了,这句话很多单调队列都用得上。

完整代码:

#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=1e6+10;
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')
          f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
inline void write(ll x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9)
	  write(x/10);
	putchar(x%10+'0');
}
ll n,q,k,j,head,tail;
ll a[N],dp[N],Q[N];
bool check(ll i,ll j){
	if(dp[i]<dp[j])
	  return 1;
	if(dp[i]==dp[j])
	  return a[i]<=a[j];
	return 0;
}
void solve(){
	head=1,tail=0;
	k=read();
	for(int i=1;i<=n;i++)
	  dp[i]=0;
	for(int j=n,i=n-1;i>=1;i--){
		while(j>i+k){
			if(Q[head]==j)
			  head++;
			j--;
		}
		while(check(i+1,Q[tail])&&head<=tail)
		  tail--;
		Q[++tail]=i+1;
		if(a[i]<=a[Q[head]])
		  dp[i]=dp[Q[head]]+1;
		else
		  dp[i]=dp[Q[head]];
	}
	write(dp[1]);
	putchar('\n');
}
bool End;
int main(){
	n=read();
	for(int i=1;i<=n;i++)
	  a[i]=read();
	q=read();
	while(q--)
	  solve();
	cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
	return 0;
}
posted @ 2024-08-03 10:10  rgw2010  阅读(9)  评论(0编辑  收藏  举报