Codeforces 474E Pillars【Dp+线段树+二分+离散化】好题~

351 篇文章 2 订阅
142 篇文章 0 订阅

E. Pillars
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Marmot found a row with n pillars. The i-th pillar has the height of hi meters. Starting from one pillar i1, Marmot wants to jump on the pillars i2, ..., ik. (1 ≤ i1 < i2 < ... < ik ≤ n). From a pillar i Marmot can jump on a pillar j only if i < j and |hi - hj| ≥ d, where |x| is the absolute value of the number x.

Now Marmot is asking you find out a jump sequence with maximal length and print it.

Input

The first line contains two integers n and d (1 ≤ n ≤ 105, 0 ≤ d ≤ 109).

The second line contains n numbers h1, h2, ..., hn (1 ≤ hi ≤ 1015).

Output

The first line should contain one integer k, the maximal length of a jump sequence.

The second line should contain k integers i1, i2, ..., ik (1 ≤ i1 < i2 < ... < ik ≤ n), representing the pillars' indices from the maximal length jump sequence.

If there is more than one maximal length jump sequence, print any.

Examples
Input
5 2
1 3 6 7 4
Output
4
1 2 3 5 
Input
10 3
2 1 3 6 9 11 7 3 20 18
Output
6
1 4 6 7 8 9 
Note

In the first example Marmot chooses the pillars 1, 2, 3, 5 with the heights 1, 3, 6, 4. Another jump sequence of length 4 is 1, 2, 4, 5.


题目大意:


给你N个数的序列,现在让你找到一个点作为起点,向后跳,希望跳的最远,让你找到这个长度,以及一条路径。

从点i能够跳到点j的条件是:i<j&&|a[i]-a[j]|>=d;


思路:


1、这种题很套路啊,我们肯定这个题一定是dp题咯,那么设定dp【i】=x表示以a【i】结尾的最长子序列长度是x。

那么状态转移方程有:dp【i】=max(1,dp【j】+1)【1<=j<i&&|a[i]-a[j]|>=d】

然而我们暴力去跑这个dp的话,肯定要超时的、那么考虑线段树优化(这种题很套路,做多了一下子就能想到了);

使得有:

dp【i】=1;

dp【i】=max(dp【i】,max【1,a[i]-d-1】);

dp【i】=max(dp【i】,max【a[i]+d+1,inf】);

如果问题中a【i】比较小的话,那么肯定我们可以直接套线段树维护即可。

但是这里a【i】比较大,那么我们肯定是需要离散化的,再考虑到a【i】的值是会重复的,那么我们一定要注意离散化的时候不能只根据值来离散化。

那么离散化之后,线段树上第i个元素就表示第i大的元素所保存的值。

那么我们在区间查询的时候,要二分两个点.然后再进行查询(因为此时我们已知的信息是每个值是第几大)。

过程谨慎一点去实现,然后每一次搞定了dp【i】之后,将dp【i】更新入树即可。


2、搞定了dp问题之后,max(dp【i】)【1<=i<=n】就是最长子序列的长度。

那么如何搞定路径问题呢?

我们考虑在查询区间最大值的同时,查询这个最大值的位子,如果dp【i】可以被查询所更新,那么我们记录pre【i】=之前那个位子即可。

过程维护一下pre【i】,输出结果的时候,回溯一下并逆序输出路径即可。


Ac代码:


#include<stdio.h>
#include<string.h>
#include<climits>
#include<map>
#include<algorithm>
using namespace std;
#define ll __int64
#define lson l, m, rt<<1
#define rson m+1, r, (rt<<1)|1
struct node
{
    ll val,pos;
}tmp[105000];
ll cmp(node a,node b)
{
    return a.val<b.val;
}
ll tree[111111*4];
ll posn[111111*4];
ll pre[111111];
ll output[111111];
void pushup(ll rt)
{
    if (tree[rt<<1] > tree[rt<<1|1])
    {
        tree[rt] = tree[rt<<1];
        posn[rt] = posn[rt<<1];
    }
    else
    {
        tree[rt] = tree[rt<<1|1];
        posn[rt] = posn[rt<<1|1];
    }
}
void build(ll l, ll r, ll rt)
{
    if (l == r)
    {
        tree[rt]=0;
        posn[rt] = l;
    }
    else
    {
        ll m = (l + r) >> 1;
        build(lson);
        build(rson);
        pushup(rt);
    }
}
void update(ll p, ll val, ll l, ll r, ll rt)
{
    if (l == r)
    {
        tree[rt] = val;
    }
    else
    {
        ll m = (l + r) >> 1;
        if (p <= m)
        {
            update(p, val, lson);
        }
        else
        {
            update(p, val, rson);
        }
        pushup(rt);
    }
}
ll query(ll L, ll R, ll l, ll r, ll rt, ll *pos)
{
    if (L <= l && r <= R)
    {
        *pos = posn[rt];
        return tree[rt];
    }
    else
    {
        ll m = (l + r) >> 1;
        ll ret1 = INT_MIN;
        ll ret2 = INT_MIN;
        ll pa, pb;
        ll *pos1 = &pa;
        ll *pos2 = &pb;
        if (L <= m)
        {
            ret1 = query(L, R,  lson, pos1);
        }
        if (R > m)
        {
            ret2 = query(L, R, rson, pos2);
        }
        if (ret1 > ret2)
        {
            *pos = pa;
        }
        else
        {
            *pos = pb;
            ret1 = ret2;
        }
        return ret1;
    }
}
ll dp[108000];
ll a[108000];
int main()
{
    ll n,d;
    while(~scanf("%I64d%I64d",&n,&d))
    {
        map<ll ,ll >s;
        memset(pre,-1,sizeof(pre));
        for(ll i=1;i<=n;i++)scanf("%I64d",&a[i]),tmp[i].val=a[i],tmp[i].pos=i;
        sort(tmp+1,tmp+1+n,cmp);
        for(ll i=1;i<=n;i++)
        {
            s[tmp[i].pos]=i;
        }
        build(1,n,1);
        for(ll i=1;i<=n;i++)
        {
            if(i==1)dp[i]=1;
            else
            {
                dp[i]=1;
                ll l=1;
                ll r=s[i];
                ll posl=r;
                while(r-l>=0)
                {
                    ll mid=(l+r)/2;
                    if(tmp[mid].val>a[i]-d)
                    {
                        posl=mid;
                        r=mid-1;
                    }
                    else l=mid+1;
                }
                l=s[i];
                r=n;
                ll posr=l;
                while(r-l>=0)
                {
                    ll mid=(l+r)/2;
                    if(tmp[mid].val<a[i]+d)
                    {
                        posr=mid;
                        l=mid+1;
                    }
                    else r=mid-1;
                }
                ll temp;
                if(posl-1>=1)
                {
                    if(query(1,posl-1,1,n,1,&temp)+1>dp[i])
                    {
                        dp[i]=query(1,posl-1,1,n,1,&temp)+1;
                        pre[i]=tmp[temp].pos;
                    }
                }
                if(n>=posr+1)
                {
                    if(query(posr+1,n,1,n,1,&temp)+1>dp[i])
                    {
                        dp[i]=query(posr+1,n,1,n,1,&temp)+1;
                        pre[i]=tmp[temp].pos;
                    }
                }
            }
            update(s[i],dp[i],1,n,1);
        }
        ll maxn=0;
        ll anspos=0;
        for(ll i=1;i<=n;i++)
        {
            if(dp[i]>maxn)maxn=dp[i],anspos=i;
        }
        printf("%I64d\n",maxn);
        int cnt=0;
        while(anspos>=1)
        {
            output[cnt++]=anspos;
            anspos=pre[anspos];
        }
        for(int i=cnt-1;i>=0;i--)
        {
            printf("%I64d ",output[i]);
        }
        printf("\n");
    }
}









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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值