• CF792E Colored Balls【思维】


    题目传送门

    考试的时候又想到了小凯的疑惑,真是中毒不浅...

    设每一个数都可以被分成若干个$k$和$k+1$的和。数$x$能够被分成若干个$k$和$k+1$的和的充要条件是:
    $x%k<=floor(x/k)$

    又因为$k$一定小于这个数列中最小的那个数,可以轻易想到的一个朴素的方法就是从$1$到$A_{min}$枚举所有可能的$k$,判断是否满足情况,并更新答案。

    注意到$k$越大,答案越优,所以从大到小进行枚举,找到答案就退出。

    我们现在来优化他:

    可以想到,当$k<=sqrt{x}$,上述不等式一定成立。

    所以只需要判断$k$在$(sqrt{x},x]$范围内是否满足就可以了。

    可是$x$在$1e9$的范围内,还是会超时呢。

    其实我们枚举到了很多无用的$k$,因为要保证$A_{min}$也可以分成若干个$k$和$k+1$的和,所以实际上有效的$k$是:$A_{min}$,$A_{min}/2$,$A_{min}/3$...诸如此类的数...

    我们可以枚举集合个数($A_{min}$可以被拆成多少个数),然后通过集合个数来算$k$

    枚举范围就从$(sqrt{x},x]$变成了$(1,sqrt{x}]$

    在代码里,我特判了一下$1$的情况(其实是因为考试稳妥)

    还有一些细节问题都放在注释里了

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<vector>
     4 #include<queue>
     5 #include<cmath>
     6 using namespace std;
     7 #define N 505
     8 #define ll long long
     9 int n;
    10 int a[N];
    11 ll ans;
    12 int rd()
    13 {
    14     int f=1,x=0;char c=getchar();
    15     while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
    16     while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    17     return f*x;
    18 }
    19 int res=-1;
    20 bool check(int k,int ret,int id)
    21 {//如果余数为0 有一次将k调整成k-1的机会 
    22     for(int i=1;i<=n;i++)
    23     {
    24         int p=a[i]/k,q=a[i]%k;
    25         if(ret&&q>p) return 0;
    26         if(!ret)
    27         {
    28             if(q>p)
    29             {
    30                 k--;
    31                 ret=1;
    32                 p=a[i]/k,q=a[i]%k;
    33             }
    34             if(q>p) return 0;
    35         }
    36     }
    37     res=k;
    38     return 1;
    39 }
    40 int main()
    41 {
    42     n=rd();
    43     for(int i=1;i<=n;i++)
    44         a[i]=rd();
    45     sort(a+1,a+n+1);
    46     if(a[1]==1)
    47     {
    48         for(int i=2;i<=n;i++)
    49         {
    50             if(a[i]&1)
    51             {
    52                 ans+=(a[i]-1)>>1;
    53                 ans++;
    54             }
    55             else ans+=(a[i]>>1);
    56         }
    57         printf("%lld
    ",ans+1);
    58         return 0;
    59     }
    60     for(int i=1;i<=int(sqrt(a[1]))+1;i++)
    61     {//枚举集合个数 (对于最小的数) 
    62         int k=a[1]/i;//集合大小 k和k+1
    63         int ret=a[1]%i;//如果是整除 就不能确定是k-1和k 还是k和k+1
    64         //如果有余数 肯定是k和k+1(k还不够) 
    65         //如果余数为0 有一次将k调整成k-1的机会 
    66         if(check(k,ret,i))
    67             break;
    68     }
    69     //printf("%d
    ",res);
    70     for(int i=1;i<=n;i++)
    71         ans+=(a[i]+res)/(res+1);
    72     printf("%lld
    ",ans);
    73     return 0;
    74 }
    75 /*
    76 2
    77 948507270 461613425
    78 */
    Code
  • 相关阅读:
    java 网络编程
    JAVA 中for-each循环使用方法
    JAVA 常用集合接口List、Set、Map总结
    android学习计划
    ExtJs
    jQuery easyui
    MVC
    简易servlet计算器
    使用servlet实现用户注册功能
    用JavaBean实现数据库的连接和关闭,在jsp页面输出数据库中student表中学生的信息
  • 原文地址:https://www.cnblogs.com/lyttt/p/11853345.html
Copyright © 2020-2023  润新知