bzoj 4582: [Usaco2016 Open]Diamond Collector (单调队列+线段树)

4582: [Usaco2016 Open]Diamond Collector

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 119   Solved: 82
[ Submit][ Status][ Discuss]

Description

Bessie the cow, always a fan of shiny objects, has taken up a hobby of mining diamonds in her spare 
time! She has collected N diamonds (N≤50,000) of varying sizes, and she wants to arrange some of th
em in a pair of display cases in the barn.Since Bessie wants the diamonds in each of the two cases t
o be relatively similar in size, she decides that she will not include two diamonds in the same case
 if their sizes differ by more than K (two diamonds can be displayed together in the same case if th
eir sizes differ by exactly K). Given K, please help Bessie determine the maximum number of diamonds
 she can display in both cases together.
给定长度为N的数列a,要求选出两个互不相交的子序列(可以不连续),满足同一个子序列中任意两个元素差的绝
对值不超过K。最大化两个子序列长度的和并输出这个值。1 ≤ N ≤ 50000, 1 ≤ a_i ≤ 10 ^ 9, 0 ≤ K ≤ 10^ 9

Input

The first line of the input file contains N and K (0≤K≤1,000,000,000). The next NN lines each cont
ain an integer giving the size of one of the diamonds. All sizes will be positive and will not excee
d 1,000,000,000

Output

Output a single positive integer, telling the maximum number of diamonds that Bessie can showcase in
 total in both the cases.

Sample Input

7 3
10
5
1
12
9
5
14

Sample Output

5

HINT

Source

[ Submit][ Status][ Discuss]


题解:单调队列+线段树

按权值排序,然后用单调队列处理出从位置i最远可以延伸到的位置。

然后根据长度建立线段树,枚举第一区间的起点,然后线段树查询区间终点后面长度最长的区间。

但是其实还可以做两遍单调队列预处理出每个位置向前最远延伸的长度和向后最远延伸的长度。

l[i]=max(l[i],l[i-1]) 

r[i]=max(r[i],r[i+1])

然后枚举断点即可。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#define N 50003
#define LL long long 
using namespace std;
int n,pos[N],ans,tr[N*4];
LL val[N],m;
int cmp(int x,int y)
{
	return x>y;
}
void update(int now)
{
	tr[now]=max(tr[now<<1],tr[now<<1|1]);
}
void build(int now,int l,int r)
{
	if (l==r) {
		tr[now]=pos[l]-l+1;
		return;
	}
	int mid=(l+r)/2;
	build(now<<1,l,mid);
	build(now<<1|1,mid+1,r);
	update(now);
}
int qjmax(int now,int l,int r,int ll,int rr)
{
	if (ll>rr) return 0;
	if(ll<=l&&r<=rr) return tr[now];
	int mid=(l+r)/2; int ans=0;
	if (ll<=mid) ans=max(ans,qjmax(now<<1,l,mid,ll,rr));
	if (rr>mid) ans=max(ans,qjmax(now<<1|1,mid+1,r,ll,rr));
	return ans; 
}
int main()
{
	freopen("diamond.in","r",stdin);
	freopen("diamond.out","w",stdout);
    scanf("%d%I64d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%I64d",&val[i]),pos[i]=i;
	int l=1; int r=1; 
	sort(val+1,val+n+1,cmp);
	while (l<=n) {
		while (val[l]-val[r+1]<=m&&r+1<=n)  
		 r++;
		pos[l]=r;
		l++;
	}	
    //for (int i=1;i<=n;i++) cout<<pos[i]<<" ";
    //cout<<endl;
    build(1,1,n);
	for (int i=1;i<=n;i++) 
	  ans=max(ans,pos[i]-i+1+qjmax(1,1,n,pos[i]+1,n));
	printf("%d\n",ans);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值