https://codeforces.com/contest/1407/problem/D
题意:给定n个高度,当
i
<
j
i<j
i<j且满足以下条件之一时,可以从
i
i
i跳到
j
j
j:
问从1到n的最小步数。
思路:设
d
p
[
j
]
dp[j]
dp[j]表示从1到
j
j
j的最小步数:
1、满足条件2的点
i
i
i:
①要保证
i
i
i~
j
j
j内的数严格小于
h
[
j
]
h[j]
h[j],设pos是向左第一个大于等于
h
[
j
]
h[j]
h[j]的数的位置,我们可以在
[
p
o
s
,
j
−
1
]
[pos,j-1]
[pos,j−1]的区间内找满足可跳条件的点
i
i
i;
②要保证
i
i
i~
j
j
j内的数严格小于
h
[
i
]
h[i]
h[i],我们每次查找区间
[
p
o
s
,
j
−
1
]
[pos,j-1]
[pos,j−1]内最大的数,那么这个最大数的位置就是
i
i
i的一个解,即
h
[
i
]
=
m
a
x
(
h
[
p
o
s
]
h[i]=max(h[pos]
h[i]=max(h[pos]~
h
[
j
−
1
]
)
h[j-1])
h[j−1]);
③不断更新
p
o
s
=
i
+
1
pos=i+1
pos=i+1,重复操作②就可以找到所有符合条件的i
2、查找满足条件3的点
i
i
i同理
3、查找第一个大于等于
h
[
j
]
h[j]
h[j]的数的位置和区间最大值都可以用线段树操作。
4、我们可以发现,在第一步中,我们每次查找的i是h[ ]中一个单调递减的子序列,而这个序列我们可以用单调队列维护,具体操作见代码AC2,这样i的每一个解就能O(1)找到。
AC1:
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<set>
#include<queue>
#include<vector>
using namespace std;
#define LL long long
#define uLL unsigned long long
#define PII pair<int,int>
#define mid ((l + r)>>1)
#define chl (root<<1)
#define chr (root<<1|1)
#define QL chl,l,mid,ll,rr
#define QR chr,mid+1,r,ll,rr
#define QL_div chl,l,mid,ll,mid
#define QR_div chr,mid+1,r,mid+1,rr
#define lowbit(x) ( x&(-x) )
const int manx = 3e5 + 10;
const int INF = 2e9;
const int mod = 1e4+7;
int n,num[manx],dp[manx];
struct node
{
int mmax,mmin;
int pos1,pos2;
}tree[manx<<2];
void eval(int root)
{
if(tree[chl].mmax>tree[chr].mmax){
tree[root].mmax=tree[chl].mmax;
tree[root].pos1=tree[chl].pos1;
}
else{
tree[root].mmax=tree[chr].mmax;
tree[root].pos1=tree[chr].pos1;
}
if(tree[chl].mmin<tree[chr].mmin){
tree[root].mmin=tree[chl].mmin;
tree[root].pos2=tree[chl].pos2;
}
else{
tree[root].mmin=tree[chr].mmin;
tree[root].pos2=tree[chr].pos2;
}
}
void build_tree(int root,int l,int r)
{
if(l==r){
tree[root].mmax=tree[root].mmin=num[l];
tree[root].pos1=tree[root].pos2=l;
return;
}
build_tree(chl,l,mid);
build_tree(chr,mid+1,r);
eval(root);
}
PII max(PII a,PII b)
{
if(a.first==b.first)return a.second>b.second?a:b;
return a.first>b.first?a:b;
}
PII min(PII a,PII b)
{
if(a.first==b.first)return a.second>b.second?a:b;
return a.first<b.first?a:b;
}
PII query_max(int root,int l,int r,int ll,int rr)
{
if(l==ll&&r==rr)
return make_pair(tree[root].mmax,tree[root].pos1);
if(rr<=mid)
return query_max(QL);
else if(ll>mid)
return query_max(QR);
else
return max(query_max(QL_div),query_max(QR_div));
}
PII query_min(int root,int l,int r,int ll,int rr)
{
if(l==ll&&r==rr)
return make_pair(tree[root].mmin,tree[root].pos2);
if(rr<=mid)
return query_min(QL);
else if(ll>mid)
return query_min(QR);
else
return min(query_min(QL_div),query_min(QR_div));
}
int query_max1(int root,int l,int r,int ll,int rr,int val)
{
if(tree[root].mmax<val)return 0;
if(l==r)return l;
if(rr<=mid)
return query_max1(QL,val);
else if(ll>mid)
return query_max1(QR,val);
else{
int pos=query_max1(QR_div,val);
if(!pos)pos=query_max1(QL_div,val);
return pos;
}
}
int query_min1(int root,int l,int r,int ll,int rr,int val)
{
if(tree[root].mmin>val)return 0;
if(l==r)return l;
if(rr<=mid)
return query_min1(QL,val);
else if(ll>mid)
return query_min1(QR,val);
else{
int pos=query_min1(QR_div,val);
if(!pos)pos=query_min1(QL_div,val);
return pos;
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&num[i]);
PII now;
int l,r;
build_tree(1,1,n);
dp[1]=0;
for(int i=2;i<=n;++i){
dp[i]=dp[i-1]+1;
if(num[i-1]<num[i]){
l=max(1,query_max1(1,1,n,1,i-1,num[i])),r=i-1;//l为第一个大于等于num[i]的数
while(l<r){
now=query_max(1,1,n,l,r);
dp[i]=min(dp[i],dp[now.second]+1);
l=now.second+1;
}
}
else if(num[i-1]>num[i]){
l=max(1,query_min1(1,1,n,1,i-1,num[i])),r=i-1;//l为第一个小于等于等于num[i]的数
while(l<r){
now=query_min(1,1,n,l,r);
dp[i]=min(dp[i],dp[now.second]+1);
l=now.second+1;
}
}
}
printf("%d\n",dp[n]);
return 0;
}
AC1:
#include<algorithm>
using namespace std;
const int manx = 3e5 + 10;
const int INF = 2e9;
int n,tail1,tail2;
int dp[manx],h[manx],up[manx],down[manx];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&h[i]);
dp[1]=0;
tail1=tail2=1;
down[1]=up[1]=1;
for(int i=2;i<=n;i++){
dp[i]=dp[i-1]+1;
while(tail1&&h[down[tail1]]<h[i]){
dp[i]=min(dp[i],dp[down[tail1]]+1);//每一个down[tail1]都是i的一个解
tail1--;//维护(h值)单调递减序列
}
if(tail1)dp[i]=min(dp[i],dp[down[tail1]]+1);//此时h[down[tail1]]是第一个大于等于h[i]的值
if(tail1&&h[down[tail1]]==h[i])tail1--;
down[++tail1]=i;
/*操作同上*/
while(tail2&&h[up[tail2]]>h[i]){
dp[i]=min(dp[i],dp[up[tail2]]+1);
tail2--;
}
if(tail2)dp[i]=min(dp[i],dp[up[tail2]]+1);
if(tail2&&h[up[tail2]]==h[i])tail2--;
up[++tail2]=i;
}
printf("%d\n",dp[n]);
return 0;
}