前言+题目描述
加入C2020退役团,享受四连测所有题的快乐!
题目加团可看
这道题怕不是猴子派来的烂题……
分析
单调队列优化DP……
状态分析
这个状态还是挺好想的……
以
d
p
i
dp_i
dpi表示到第
i
i
i号的最小花费
DP分析
首先可以写出一个非常暴力的DP转移方程,如下
d
p
i
=
m
i
n
{
d
p
j
+
(
h
j
<
=
h
i
)
}
dp_i=min\{dp_j+(h_j<=h_i)\}
dpi=min{dpj+(hj<=hi)}
应该看的人都可以理解吧
找一个
j
j
j,使
i
i
i到
j
j
j的距离最小,显然
d
p
j
+
(
h
j
<
=
h
i
)
dp_j+(h_j<=h_i)
dpj+(hj<=hi)就是这个距离。
不过,这样做是
O
(
n
2
Q
)
O(n^2Q)
O(n2Q)的,不超时才怪……
单调队列
不会请右转我的这篇博客
对于任意两个决策点
j
j
j和
k
k
k来说,如果
d
p
j
<
d
p
k
dp_j<dp_k
dpj<dpk,那么转移到
k
k
k绝不会优于转移到
j
j
j(到
j
j
j最多劳累值只加1)
如果
d
p
j
=
d
p
k
dp_j=dp_k
dpj=dpk,那么直接比较
h
i
h_i
hi和
h
j
h_j
hj即可得到谁更优
于是,我们就可以用一个单调队列来储存了,就变成了一个滑动窗口(不会同样请看上博客)的问题——距离为输入的
K
K
K,找单调队列中的最小值即可,小于的定义即为
d
p
j
<
d
p
k
∥
(
d
p
j
=
d
p
k
&
&
h
j
>
h
k
)
dp_j<dp_k\ \|\ (dp_j=dp_k\&\&h_j>h_k)
dpj<dpk ∥ (dpj=dpk&&hj>hk)则
j
j
j更优
时间复杂度为
O
(
n
Q
)
O(nQ)
O(nQ),
n
n
n为
1
0
6
10^6
106,
Q
Q
Q为
25
25
25,
2.5
∗
1
0
7
2.5*10^7
2.5∗107还是可以在
2
2
2秒时限下卡过的
代码
#include<map>
#include<set>
#include<list>
#include<queue>
#include<deque>
#include<stack>
#include<ctime>
#include<cmath>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cctype>
#include<string>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<iomanip>
#include<iostream>
#include<algorithm>
using namespace std;
#define reg register
template <typename T>
inline T read() {
T a=0; char c=getchar(),f=1;
while(c<'0'||c>'9') {
if(c=='-') f=-f;
if(c==-1) return c;
c=getchar();
}
while(c>='0'&&c<='9') a=(a<<1)+(a<<3)+(c^48),c=getchar();
return a*f;
}
template <class T>
inline int write(T x) {
if(x<0) x=(~x)+1, putchar('-');
if(x/10) write(x/10);
return putchar(x%10|48);
}
template <class T>
inline int write(T x,char c) {
return write(x)&&putchar(c);
}
template <class T>
inline T Max(T a,T b) { return a>b?a:b; }
template <class T>
inline T Min(T a,T b) { return a<b?a:b; }
template <class T>
inline T Abs(T a) { return a<0?-a:a; }
const int MAXN=1000005;
int n;
int h[MAXN];
int dp[MAXN];
int dq[MAXN],fnt,bck;
inline void init() {
n=read<int>();
for(reg int i=1;i<=n;i++)
h[i]=read<int>();
}
inline bool comp(int a,int b) { //返回a优于b
return dp[a]<dp[b]||(dp[a]==dp[b]&&h[a]>h[b]);
}
inline void solve() {
int Q=read<int>();
while(Q--) {
memset(dp,0x3f,sizeof dp);
dp[1]=0;
fnt=1; bck=0;
int k=read<int>();
dq[++bck]=1;
for(reg int i=2;i<=n;i++) {
while(fnt<=bck&&dq[fnt]<i-k)
fnt++;
dp[i]=dp[dq[fnt]]+(h[i]>=h[dq[fnt]]);
while(fnt<=bck&&comp(i,dq[bck]))
bck--;
dq[++bck]=i;
}
write(dp[n],'\n');
}
}
int main() {
init();
solve();
}