zoukankan      html  css  js  c++  java
  • Codeforces Round #424 (Div. 2, rated, based on VK Cup Finals) Problem F (Codeforces 831F)

    题目传送门

      传送门I

      传送门II

      传送门III

    题目大意

      求一个满足$dsum_{i = 1}^{n} left lceil frac{a_i}{d} ight ceil - sum_{i = 1}^{n} a_{i} leqslant K$的最大正整数$d$。

      整理一下可以得到条件是$dsum_{i = 1}^{n} left lceil frac{a_i}{d} ight ceil leqslant K + sum_{i = 1}^{n} a_{i}$

      有注意到$left lceil frac{a_i}{d} ight ceil$的取值个数不会超过$2left lceil sqrt{a_i} ight ceil$。

      证明考虑对于$1 leqslant d leqslant left lceil sqrt{a_i} ight ceil$,至多根号种取值,当$d >left lceil sqrt{a_i} ight ceil$的时候,取值至多为$1, 2, cdots, left lceil sqrt{a_i} ight ceil$,所以总共不会超过$2left lceil sqrt{a_i} ight ceil$个取值。

      所以我们把所有$left lceil frac{a_i}{d} ight ceil$的取值当成一个点,安插在数轴上,排个序,就愉快地找到了所有分段了。因为每一段内的$d$都是等价的,所以只需要用每一段的左端点计算和,然后判断$left lfloor frac{K + sum_{i = 1}^{n} a_{i}}{sum_{i = 1}^{n} left lceil frac{a_i}{d} ight ceil} ight floor$是否在区间内,如果是就用它去更新答案。

    Code

      1 /**
      2  * Codeforces
      3  * Problem#831F
      4  * Accepted
      5  * Time:997ms
      6  * Memory:100400k
      7  */
      8 #include <iostream>
      9 #include <cstdio>
     10 #include <ctime>
     11 #include <cmath>
     12 #include <cctype>
     13 #include <cstring>
     14 #include <cstdlib>
     15 #include <fstream>
     16 #include <sstream>
     17 #include <algorithm>
     18 #include <map>
     19 #include <set>
     20 #include <stack>
     21 #include <queue>
     22 #include <vector>
     23 #include <stack>
     24 #ifndef WIN32
     25 #define Auto "%lld"
     26 #else
     27 #define Auto "%I64d"
     28 #endif
     29 using namespace std;
     30 typedef bool boolean;
     31 const signed int inf = (signed)((1u << 31) - 1);
     32 const signed long long llf = (signed long long)((1ull << 63) - 1);
     33 const double eps = 1e-6;
     34 const int binary_limit = 128;
     35 #define smin(a, b) a = min(a, b)
     36 #define smax(a, b) a = max(a, b)
     37 #define max3(a, b, c) max(a, max(b, c))
     38 #define min3(a, b, c) min(a, min(b, c))
     39 template<typename T>
     40 inline boolean readInteger(T& u){
     41     char x;
     42     int aFlag = 1;
     43     while(!isdigit((x = getchar())) && x != '-' && x != -1);
     44     if(x == -1) {
     45         ungetc(x, stdin);    
     46         return false;
     47     }
     48     if(x == '-'){
     49         x = getchar();
     50         aFlag = -1;
     51     }
     52     for(u = x - '0'; isdigit((x = getchar())); u = (u << 1) + (u << 3) + x - '0');
     53     ungetc(x, stdin);
     54     u *= aFlag;
     55     return true;
     56 }
     57 
     58 #define LL long long
     59 
     60 int n;
     61 LL C;
     62 int* a;
     63 vector<LL> seg;
     64 
     65 template<typename T>
     66 T ceil(T a, T b) {
     67     return (a + b - 1) / b;
     68 }
     69 
     70 inline void init() {
     71     readInteger(n);
     72     readInteger(C);
     73     seg.push_back(1);
     74     a = new int[(n + 1)];
     75     for(int i = 1, x; i <= n; i++) {
     76         readInteger(a[i]);
     77         for(int j = 1; j * j <= a[i]; j++)
     78             seg.push_back(j), seg.push_back(ceil(a[i], j));
     79         C += a[i];
     80     }
     81     seg.push_back(llf);
     82 }
     83 
     84 LL res = 1;
     85 inline void solve() {
     86     sort(seg.begin(), seg.end());
     87     int m = unique(seg.begin(), seg.end()) - seg.begin() - 1;
     88     for(int i = 0; i < m; i++) {
     89         LL l = seg[i], r = seg[i + 1], temp = 0;
     90         for(int i = 1; i <= n; i++)
     91             temp += ceil((LL)a[i], l);
     92         LL d = C / temp;
     93         if(d >= l && d < r && d > res)
     94             res = d;
     95     }
     96     printf(Auto"
    ", res);
     97 }
     98 
     99 int main() {
    100     init();
    101     solve();
    102     return 0;
    103 }
    Brute force

      然后我们来讲点神仙做法。142857 orz orz orz.....

      不妨设$C = K + sum_{i = 1}^{n} a_{i}$

      因为所有$left lceil frac{a_i}{d} ight ceil geqslant 1$,所以$dleqslant left lfloor frac{C}{n} ight floor$

      设$d_0 =  left lfloor frac{C}{n} ight floor$。

      假装已经顺利地求出了$d_0, d_1, d_2, cdots, d_k$,我们找到最大的$d_{k + 1}$满足:

    $d_{k + 1}sum_{i = 1}^{n} left lceil frac{a_i}{d_k} ight ceil leqslant  C$

      当$d_{k + 1} = d_k$的时候我们就找到最优解了。

      感觉很玄学?那我来证明一下。

    定理1 $d_{k + 1} leqslant d_{k}$

      证明

    • 当$k = 0$的时候,因为$sum_{i = 1}^{n} left lceil frac{a_i}{d_0} ight ceil geqslant n$,所以$d_{1} = left lfloor frac{C}{sum_{i = 1}^{n} left lceil frac{a_i}{d_0} ight ceil} ight floor leqslant left lfloorfrac{C}{n} ight floor = d_0$。
    • 当$k > 0$的时候,假设当$k = m - 1, (m geqslant 0)$时成立,考虑当$k = m$的时候,由$d_{k} leqslant d_{k - 1}$可得$sum_{i = 1}^{n} left lceil frac{a_i}{d_{k - 1}} ight ceil leqslant sum_{i = 1}^{n} left lceil frac{a_i}{d_{k}} ight ceil $,然后可得$d_{k + 1} leqslant d_k$。

    定理2 当$d_{k + 1} = d_{k}$,$d_k$是最优解

      证明 假设存在$d > d_k$满足条件

    • 显然$d leqslant d_0$。(不然直接不合法)
    • 显然$d eq d_j   (0 leqslant j < k)$。
    • 假设$d_{j} < d < d_{j - 1} (0 < j leqslant k)$,那么$C geqslant dsum_{i = 1}^{n} left lceil frac{a_i}{d} ight ceil geqslant dsum_{i = 1}^{n} left lceil frac{a_i}{d_{j}} ight ceil $
      由迭代做法可知$dleqslant d_{j + 1}leqslant d_j$,矛盾。

      显然$d = d_k$时是合法的,所以$d_k$是最优解。

      时间复杂度感觉很低,但是我只会证它不超过$O(nsqrt{frac{C}{n}})$

    Code

     1 /**
     2  * Codeforces
     3  * Problem#831F
     4  * Accepted
     5  * Time: 31ms
     6  * Memory: 0k
     7  */
     8 #include <iostream>
     9 #include <cassert>
    10 #include <cstdlib>
    11 #include <cstdio>
    12 #include <vector>
    13 #include <queue>
    14 #ifndef WIN32
    15 #define Auto "%lld"
    16 #else
    17 #define Auto "%I64d"
    18 #endif
    19 using namespace std;
    20 typedef bool boolean;
    21 
    22 #define ll long long
    23 
    24 const int N = 105;
    25 
    26 int n;
    27 ll C;
    28 int ar[N];
    29 
    30 inline void init() {
    31     scanf("%d"Auto, &n, &C);
    32     for (int i = 0; i < n; i++)
    33         scanf("%d", ar + i), C += ar[i];
    34 }
    35 
    36 ll ceil(ll a, ll b) {
    37     return (a + b - 1) / b;
    38 }
    39 
    40 inline void solve() {
    41     ll dcur = C / n, dans;
    42     do {
    43         swap(dcur, dans), dcur = 0;
    44         for (int i = 0; i < n; i++)
    45             dcur += ceil(ar[i], dans);
    46         dcur = C / dcur;
    47     } while (dcur != dans);
    48     printf(Auto"
    ", dans);
    49 }
    50 
    51 int main() {
    52     init();
    53     solve();
    54     return 0;
    55 }

    更新日志

    • 2018-1-28 补上jmr的做法
    • 2018-10-22 给出证明
  • 相关阅读:
    git升级
    redis集群
    redis安装
    escript
    git搭建仓库与服务器
    svnsync
    post_commit.sh
    nvm安装和使用
    quartz 定时器
    Oracle flashback恢复误删的数据或表
  • 原文地址:https://www.cnblogs.com/yyf0309/p/7193163.html
Copyright © 2011-2022 走看看