• [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
    ",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 }
  • 相关阅读:
    自动化遍历-appcrawler
    Android adb实现原理
    win安装appium
    appium_android-常见的问题
    IOS项目目录结构
    svn命令的使用
    什么时候需要将析构函数定义为虚函数,如果不这么做,会存在什么问题?
    关于C++的疑问剖析
    Apple-Watch开发2 APPIcon设置
    Apple-Watch开发1
  • 原文地址:https://www.cnblogs.com/ww3113306/p/8762976.html
Copyright © 2020-2023  润新知