【Codeforces Round 271 (Div 2)E】【离散化线段树】Pillars 最长连续序列使得序列相邻的数差值至少为k

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 ≤ 1050 ≤ 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.

Sample test(s)
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 1235 with the heights 1364. Another jump sequence of length 4 is 1245.


#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}
const int N=1e5+10,Z=1e9+7,ms63=1061109567;
int n,k;
LL a[N],b[N];
int pre[N];
int O,V,P,Pre;
struct C
{
	int l,r;
	int v,p;
}c[1<<18];
void build(int o,int l,int r)
{
	c[o].l=l;
	c[o].r=r;
	c[o].v=0;
	c[o].p=0;
	if(l==r)return;
	int m=(l+r)>>1;
	build(ls,l,m);
	build(rs,m+1,r);
}
void pushup(int o)
{
	if(c[ls].v>=c[rs].v)
	{
		c[o].v=c[ls].v;
		c[o].p=c[ls].p;
	}
	else
	{
		c[o].v=c[rs].v;
		c[o].p=c[rs].p;
	}
}
void update(int o,int l,int r)
{
	if(c[o].v<=V)return;
	if(c[o].l==l&&c[o].r==r)
	{
		V=c[o].v;
		Pre=c[o].p;
		return;
	}
	int m=(c[o].l+c[o].r)>>1;
	if(r<=m)update(ls,l,r);
	else if(l>m)update(rs,l,r);
	else
	{
		update(ls,l,m);
		update(rs,m+1,r);
	}
}
void modify(int o)
{
	if(c[o].l==c[o].r)
	{
		c[o].v=V;
		c[o].p=P;
		return;
	}
	int m=(c[o].l+c[o].r)>>1;
	O<=m?modify(ls):modify(rs);
	pushup(o);
}
void print(int p)
{
	if(pre[p])print(pre[p]);
	printf("%d ",p);
}
int main()
{
	while(~scanf("%d%d",&n,&k))
	{
		for(int i=1;i<=n;++i)
		{
			scanf("%lld",&a[i]);
			b[i]=a[i];
		}
		sort(b+1,b+n+1);
		int m=unique(b+1,b+n+1)-b-1;
		build(1,1,m);
		int ansv=0;
		int ansp;
		for(int i=1;i<=n;++i)
		{
			int l=upper_bound(b+1,b+m+1,a[i]-k)-1-b;
			int r=lower_bound(b+1,b+m+1,a[i]+k)-b;
			V=0;Pre=0;
			if(1<=l)update(1,1,l);
			if(r<=m)update(1,r,m);
			++V;pre[i]=Pre;
			if(V>ansv){ansv=V;ansp=i;}
			O=lower_bound(b+1,b+m+1,a[i])-b;P=i;
			modify(1);
		}
		printf("%d\n",ansv);
		print(ansp);puts("");
	}
	return 0;
}
/*
【trick&&吐槽】
线段树已经是我的压箱底了,23333

【题意】
给你一个长度为n的数列。
每个数的数值都在[1,1e15]范围。
我们想找到最大长度的数列,使得该数列相邻两个数的绝对值之差都至少为d(0<=d<=1e9)

【类型】
离散化线段树

【分析】
首先这题是很难贪心的。
于是我们要想办法利用数据结构维护。

每个数的前驱,下标在它之前,这个是很好保证的。
然而,并非就是在前驱中,选择一个权值最大的前驱。
我们还有其他限制条件,就是两者的绝对值之差至少为k。
如何保证这个呢?

我们从"恰好"开始找突破。
对于一个数,有恰好比它小k的数,有恰好比它大k的数。
这个范围内的前驱我们是不能取的,除此之外的数我们都能取。

这些数的数值比较分散。
我们该怎么办?离散化!

这里具体而言有两种做法——
做法一,是把每个数恰好小k和恰好大k的数,都插入线段树。
这么我们就知道,对于每个数,它能取哪些区间段的数作为它的前驱了。

然而这个做法会使得空间扩大为三倍。
我们能不能不插入这两个特殊点呢?
不插入的话,我们对于[x-k,x+k]的区间段,
第一个左区间段合法的数就是upper_bound(x-k)-1
第一个右区间段外合法的数就是lower_bound(x+k)
我们只要从这2个区间段更新出最大权值的前驱即可。

然后还有一个需要处理的问题,就是我们还要输出路径。
这个要如何实现呢?我们在更新区间最大值的同时,同时更新位点(这个位点是基于原始数组下标的)就好啦。

【时间复杂度&&优化】
O(nlogn)

*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值