Codeforces Round #271 (Div. 2) E Pillars(dp+线段树优化)

本文介绍如何利用线段树优化算法解决特定条件下的数组元素比较问题,具体为当abs(d[i]-d[j])>=d时,通过线段树快速查询最大值dp,并提供了AC代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目要求abs(d[i]-d[j])>=d,也就是说只要a[j]<=a[i]-d或者a[i]+d<=a[j] ,也就是只要求1~a[i]-d即a[i]+d~max里最大的dp值,这就是一个线段树查询问题。建议先看一下本博客里面LIS线段树优化的文章。

下面是AC代码:

  

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int dp[100005],pre[100005];
LL s[100005],a[100005];
int b[100005];
struct pi{
    int le;
    int ri;
    int ma;
}pp[400005];
void init(int tot,int l,int r){
    pp[tot].ma=-1;
    pp[tot].le=l;
    pp[tot].ri=r;
    if(l==r) return ;
    init(2*tot,l,(l+r)/2);
    init(2*tot+1,(l+r)/2+1,r);
}
void merg(int tot,int p,int n){
    if(pp[tot].le==pp[tot].ri){
        if(pp[tot].ma==-1) pp[tot].ma=n;
        else{
            if(dp[pp[tot].ma]<dp[n]){
                pp[tot].ma=n;
            }
        }
        return ;
    }
    int mid=(pp[tot].le+pp[tot].ri)/2;
    if(p<=mid) merg(2*tot,p,n);
    else merg(2*tot+1,p,n);
    if(pp[2*tot].ma==-1) pp[tot].ma=pp[2*tot+1].ma;
    if(pp[2*tot+1].ma==-1) pp[tot].ma=pp[2*tot].ma;
    if(dp[pp[2*tot].ma]<dp[pp[2*tot+1].ma]) pp[tot].ma=pp[2*tot+1].ma;
    else pp[tot].ma=pp[2*tot].ma;
}
int query(int tot,int l,int r){
    int x,y;
    if(l<=pp[tot].le&&r>=pp[tot].ri) return pp[tot].ma;
    int mid;
    mid=(pp[tot].le+pp[tot].ri)/2;
    x=-1;
    y=-1;
    if(l<=mid) x=query(2*tot,l,r);
    if(r>mid) y=query(2*tot+1,l,r);
    if(x==-1) return y;
    if(y==-1) return x;
    if(dp[x]<dp[y]) return y;
    return x;
}
int get(int n,LL k){
    int le,ri,mid;
    le=1;
    ri=n;
    while(le<=ri){
        mid=(le+ri)/2;
        if(s[mid]<k) le=mid+1;
        else ri=mid-1;
    }
    return le;
}
int main()
{
    int i,j,n,m,p,k,d,x,y;
    LL a1,b1;
    cin>>n>>d;
    for(i=1;i<=n;i++){
        cin>>a[i];
        s[i]=a[i];
    }
    sort(s+1,s+1+n);
    dp[0]=0;
    init(1,1,n);
    memset(pre,-1,sizeof(pre));
    for(i=1;i<=n;i++){
        if(i==1){
            dp[1]=1;
            p=get(n,a[i]);
            merg(1,p,1);
        }
        else{
            x=-1;
            y=-1;
            a1=a[i]-d;
            if(a1>0){
                p=get(n,a1);
                if(p>n||s[p]!=a1) p--;
            }
            else p=0;
            if(p>0)
            x=query(1,1,p);
            b1=a[i]+d;
            p=get(n,b1);
            if(p<=n) y=query(1,p,n);
            if(x==-1&&y==-1) dp[i]=1;
            else{
                if(x==-1){
                    dp[i]=dp[y]+1;
                    pre[i]=y;
                }
                else if(y==-1){
                    dp[i]=dp[x]+1;
                    pre[i]=x;
                }
                else{
                    if(dp[x]<dp[y]){
                        dp[i]=dp[y]+1;
                        pre[i]=y;
                    }
                    else {
                        dp[i]=dp[x]+1;
                        pre[i]=x;
                    }
                }
            }
            p=get(n,a[i]);
            merg(1,p,i);
        }
    }
    p=0;
    for(i=1;i<=n;i++){
        if(dp[i]>p) p=dp[i];
    }
    int tot;
    printf("%d\n",p);
    tot=0;
    for(i=1;i<=n;i++){
        if(dp[i]==p){
            b[tot++]=i;
            i=pre[i];
            while(i!=-1){
                b[tot++]=i;
                i=pre[i];
            }
            break;
        }
    }
    for(i=tot-1;i>=0;i--) printf(" %d",b[i]);
    printf("\n");
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值