Codeforces Round #424 (Div. 2, rated, based on VK Cup Finals) Problem F (Codeforces 831F) - 数论 - 暴力

题目传送门

  传送门I

  传送门II

  传送门III

题目大意

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

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

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

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

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

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"\n", 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} \right \rceil \geqslant 1$,所以$d\leqslant \left \lfloor \frac{C}{n} \right \rfloor$

  设$d_0 =  \left \lfloor \frac{C}{n} \right \rfloor$。

  假装已经顺利地求出了$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} \right \rceil \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} \right \rceil \geqslant n$,所以$d_{1} = \left \lfloor \frac{C}{\sum_{i = 1}^{n} \left \lceil \frac{a_i}{d_0} \right \rceil} \right \rfloor \leqslant \left \lfloor\frac{C}{n}\right \rfloor = 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}} \right \rceil \leqslant \sum_{i = 1}^{n} \left \lceil \frac{a_i}{d_{k}} \right \rceil $,然后可得$d_{k + 1} \leqslant d_k$。

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

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

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

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

  时间复杂度感觉很低,但是我只会证它不超过$O(n\sqrt{\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"\n", dans);
49 }
50 
51 int main() {
52     init();
53     solve();
54     return 0;
55 }

更新日志

  • 2018-1-28 补上jmr的做法
  • 2018-10-22 给出证明
posted @ 2017-07-17 10:35  阿波罗2003  阅读(921)  评论(0编辑  收藏  举报