cf474e Pillars(线段树优化dp)

17 篇文章 0 订阅
3 篇文章 0 订阅

题意

有n个柱子,每个柱子有一个高度 hi h i ,每个柱子可以跳到它后面高度与它相差大于d的柱子(即 |hihj|d | h i − h j | ≥ d
求最多可以跳多少个柱子


题解

ansi=max(ansj+1)(|hihj|>=dj<i) a n s i = m a x ( a n s j + 1 ) ( | h i − h j | >= d 且 j < i )
因此建立一棵线段树,节点表示高度为i的点
每次查询到一个数, ansi a n s i 等于1~ hid h i − d hi+d h i + d 中最大数的最大值+1
线段树维护最大值和最大值是哪个点


代码

#include <cstdio>
#include <algorithm>
using namespace std;
#define N 110000
long long a[N],b[N],d,ans[N];
int h1[N],h2[N],n,m,h[N],mx=0,data[N],tot=0;
struct node{int l,r,pos;long long mx;}tree[4*N]; 
void build(int v,int l,int r){
    tree[v].l=l;tree[v].r=r;tree[v].mx=0;
    if(l==r) return;int mid=l+r>>1;
    build(v<<1,l,mid);build(v<<1|1,mid+1,r);
}
int query(int v,int l,int r){
    if(l<=tree[v].l && tree[v].r<=r) return tree[v].pos;
    int mid=tree[v].l+tree[v].r>>1;int ans1=0,ans2=0;
    if(l<=mid) ans1=query(v<<1,l,r);
    if(mid<r) ans2=query(v<<1|1,l,r);
    if(tree[ans1].mx>=tree[ans2].mx) return ans1;
    else return ans2;
}
void update(int v){
    tree[v].mx=max(tree[v<<1].mx,tree[v<<1|1].mx);
    if(tree[v<<1].mx>=tree[v<<1|1].mx) tree[v].mx=tree[v<<1].mx,tree[v].pos=tree[v<<1].pos;
    else tree[v].mx=tree[v<<1|1].mx,tree[v].pos=tree[v<<1|1].pos;
}
void add(int v,int l,long long x){
    if(l==tree[v].l && l==tree[v].r){tree[v].mx=x;tree[v].pos=v;return;}
    int mid=tree[v].l+tree[v].r>>1;
    if(l<=mid) add(v<<1,l,x);
    if(mid<l) add(v<<1|1,l,x);
    update(v);
}
int main(){
    scanf("%d%d",&n,&d);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]),b[i]=a[i];a[n+1]=b[n+1]=(long long)1000000000000001;
    sort(b+1,b+n+2);m=unique(b+1,b+n+2)-b-1;
    for(int i=1;i<=m-1;i++){
        h1[i]=upper_bound(b+1,b+m+1,b[i]-d)-b-1;
        h2[i]=lower_bound(b+1,b+m+1,b[i]+d)-b;
    }
    build(1,1,m-1);
    for(int i=1;i<=n;i++){
        a[i]=lower_bound(b+1,b+m+1,a[i])-b;int y=0,z=0;
        if(h1[a[i]]) y=query(1,1,h1[a[i]]);
        if(h2[a[i]]<m) z=query(1,h2[a[i]],m-1);
        if(tree[y].mx>=tree[z].mx) h[i]=tree[y].l,ans[i]=tree[y].mx+1;
        else h[i]=tree[z].l,ans[i]=tree[z].mx+1;
        add(1,a[i],ans[i]);
        if(ans[i]>ans[mx]) mx=i;
    }printf("%d\n",ans[mx]);
    int t=h[mx];data[++tot]=mx;
    for(int i=mx-1;i>=1;i--){
        if(a[i]==t){
            data[++tot]=i;
            t=h[i];
            if(!t)break;
        }
    }
    for(int i=tot;i>=1;i--) printf("%d ",data[i]); 
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值