[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表即可
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 50020 5 /*二分查找f[i],以i为开头最多能放几个,然后对f数组建立ST表, 6 查询f[f[i]+i] ~ f[n]的最大值, 7 为什么不用查询前面的? 8 与枚举数对同理,如果足够优的话,前面的会查询到后面的,所以不必重复查询(而且也不好处理), 9 这样的话,时间复杂度O(nlogn(获取f数组) + nlogn(获取ST表) + n(查询)) 10 O(nlogn+n)? --- > O(nlogn)*/ 11 int n,ans,k; 12 int ST[AC][17],f[AC],a[AC],p[AC]; 13 inline int read() 14 { 15 int x=0;char c=getchar(); 16 while(c>'9' || c<'0') c=getchar(); 17 while(c>='0' && c<='9') x=x*10+c-'0',c=getchar(); 18 return x; 19 } 20 21 inline void upmax(int &a,int b) 22 { 23 if(b>a) a=b; 24 } 25 26 inline int Min(int a,int b) 27 { 28 if(a<b) return a; 29 else return b; 30 } 31 32 inline int Max(int a,int b) 33 { 34 if(a>b) return a; 35 else return b; 36 } 37 38 inline int half(int x)//二分查找小于等于x的最大值,并返回下标 39 { 40 int l=1,r=n,mid; 41 while(l<r) 42 { 43 mid=(l+r+1)>>1;//由于符合条件时应尽可能向右移,这时为了保持ans在区间内, 44 //---> l=mid,但这样的话,由于传统mid偏向l,将会导致死循环 45 //因此将mid手动偏向r,这时由于不符合才会移动r,所以--->r=mid-1,本就不需要mid, 46 //因此每次转移必定会有偏移量,所以就不会死循环了 47 if(a[mid] <= x) l=mid;//由于mid偏向l,所以+1防止死循环 48 else r=mid-1; 49 } 50 return l; 51 } 52 53 void pre() 54 { 55 n=read(),k=read(); 56 for(R i=1;i<=n;i++) a[i]=read(); 57 sort(a+1,a+n+1); 58 for(R i=1;i<=n;i++) f[i]=half(k+a[i]) - i + 1;//f[i]存个数 59 } 60 61 void built() 62 { 63 int key=1; 64 for(R i=1;i<=n;i++) 65 { 66 if(i==(key<<1)) p[i]=p[i-1]+1,key<<=1; 67 else p[i]=p[i-1];//存下长度为i时,最长包含2的几次方,以保证一定包含了整个区间 68 ST[i][0]=f[i]; 69 } 70 for(R j=1;j<=17;j++) 71 for(R i=1;i<=n;i++) 72 ST[i][j]=Max(ST[i][j-1] , ST[Min(i + (1 << (j - 1)),n)][j-1]);//因为可能剩下的i~n,根本就不够1<<j,所以要取Min 73 } 74 75 void work() 76 { 77 for(R i=1;i<=n;i++) 78 { 79 int l=f[i] + i,r=n; 80 k=p[r-l+1]; 81 upmax(ans,f[i] + Max(ST[l][k],ST[r - (1<<k) +1][k])); 82 } 83 printf("%d\n",ans); 84 } 85 86 int main() 87 { 88 freopen("in.in","r",stdin); 89 pre(); 90 built(); 91 work(); 92 fclose(stdin); 93 return 0; 94 }