[USACO16OPEN]钻石收藏家Diamond Collector

由于相差不超过k才可以放在一起,要判断不超过k这个条件,显然我们需要排序

首先我们需要一个f数组,f[i]意义看代码开头注释,

假设我们可以选择的某一个区间是a[l]~a[r](已排序且最优(最长的意思)),那么这个区间要符合这样一个性质:

a[r]-a[l]<=k

移项一下,

a[r]<=k+a[l](r要尽可能大)

而a[l]中的l我们是枚举得到的,因此我们只需要求出对应的r即可

我们观察移项后的式子可以发现,我们要求的r其实是数列中小于等于k+a[l]的最大值

那么这显然是符合可二分性的,

所以我们二分查找r的位置,由此可以得到f[l]=half(k+a[l]) - l + 1;

然后我们可以枚举第一个架子放的是从哪里开始的

因为不允许重叠,因此我们需要查询f[f[i]+i]~f[n]的最大值(第二个架子),

为什么不用查询前面的?

与枚举数对同理,如果足够优的话,前面的会查询到后面的,所以不必重复查询(而且也不好处理,因为不知道前面查询到的会不会和后面冲突),

那么区间查询最大值,无修改,我们可以想到什么呢?

ST表!

那么ST表是什么?

ST[i][j]存从i开始往后面2^j个(包括i自己)的最大值

根据倍增的思想,我们可以在nlogn的时间内建出ST表

然后再次根据倍增的思想,我们可以实现O(1)查询

这里ST表就不作过多解释了,还不会的去写模板ST表即可

 #include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 50020
/*二分查找f[i],以i为开头最多能放几个,然后对f数组建立ST表,
查询f[f[i]+i] ~ f[n]的最大值,
为什么不用查询前面的?
与枚举数对同理,如果足够优的话,前面的会查询到后面的,所以不必重复查询(而且也不好处理),
这样的话,时间复杂度O(nlogn(获取f数组) + nlogn(获取ST表) + n(查询))
O(nlogn+n)? --- > O(nlogn)*/
int n,ans,k;
int ST[AC][],f[AC],a[AC],p[AC];
inline int read()
{
int x=;char c=getchar();
while(c>'' || c<'') c=getchar();
while(c>='' && c<='') x=x*+c-'',c=getchar();
return x;
} inline void upmax(int &a,int b)
{
if(b>a) a=b;
} inline int Min(int a,int b)
{
if(a<b) return a;
else return b;
} inline int Max(int a,int b)
{
if(a>b) return a;
else return b;
} inline int half(int x)//二分查找小于等于x的最大值,并返回下标
{
int l=,r=n,mid;
while(l<r)
{
mid=(l+r+)>>;//由于符合条件时应尽可能向右移,这时为了保持ans在区间内,
//---> l=mid,但这样的话,由于传统mid偏向l,将会导致死循环
//因此将mid手动偏向r,这时由于不符合才会移动r,所以--->r=mid-1,本就不需要mid,
//因此每次转移必定会有偏移量,所以就不会死循环了
if(a[mid] <= x) l=mid;//由于mid偏向l,所以+1防止死循环
else r=mid-;
}
return l;
} void pre()
{
n=read(),k=read();
for(R i=;i<=n;i++) a[i]=read();
sort(a+,a+n+);
for(R i=;i<=n;i++) f[i]=half(k+a[i]) - i + ;//f[i]存个数
} void built()
{
int key=;
for(R i=;i<=n;i++)
{
if(i==(key<<)) p[i]=p[i-]+,key<<=;
else p[i]=p[i-];//存下长度为i时,最长包含2的几次方,以保证一定包含了整个区间
ST[i][]=f[i];
}
for(R j=;j<=;j++)
for(R i=;i<=n;i++)
ST[i][j]=Max(ST[i][j-] , ST[Min(i + ( << (j - )),n)][j-]);//因为可能剩下的i~n,根本就不够1<<j,所以要取Min
} void work()
{
for(R i=;i<=n;i++)
{
int l=f[i] + i,r=n;
k=p[r-l+];
upmax(ans,f[i] + Max(ST[l][k],ST[r - (<<k) +][k]));
}
printf("%d\n",ans);
} int main()
{
freopen("in.in","r",stdin);
pre();
built();
work();
fclose(stdin);
return ;
}
上一篇:【BZOJ 4582】【Usaco2016 Open】Diamond Collector


下一篇:【洛谷P3143】Diamond Collector