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;
}