CF #669 (Div. 2)D. Discrete Centrifugal Jumps(线段树/单调队列优化dp)

11 篇文章 0 订阅
7 篇文章 1 订阅

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,j1]的区间内找满足可跳条件的点 i i i
②要保证 i i i~ j j j内的数严格小于 h [ i ] h[i] h[i],我们每次查找区间 [ p o s , j − 1 ] [pos,j-1] [pos,j1]内最大的数,那么这个最大数的位置就是 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[j1])
③不断更新 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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值