题解 P7585 [COCI2012-2013#1] LJUBOMORA

题目描述

一家弹珠厂向一所幼儿园捐赠了一些弹珠,弹珠一共有 \(M\) 种颜色,每颗弹珠都有一种颜色。老师需要把所有的弹珠分给 \(N\) 个孩子。每个孩子得到的所有弹珠都必须是相同的颜色,而且可以有一些孩子一颗弹珠也没得到。

我们把嫉妒值定义为分给一个孩子最多的弹珠数量。请你帮助老师分弹珠,使得嫉妒值最小

例如,如果有 \(4\) 个红色的弹珠(\(\texttt{RRRR}\))和 \(7\) 个蓝色的弹珠(\(\texttt{BBBBBBB}\)),要分给 \(5\) 个孩子,那么我们可以这样划分:\(\texttt{RR}\),\(\texttt{RR}\),\(\texttt{BB}\),\(\texttt{BB}\),\(\texttt{BBB}\)。这样分的嫉妒值为 \(3\),是最小的。

\(1 \leq m \leq 3\times 10^5 ,1 \leq n \leq 10^9 ,1 \leq x \leq 10^9\) 。 \(x\) 表示每种弹珠的数量。

Solution

如果直接计算,感觉非常不可做的样子。

但是容易发现:在每个分到弹珠的孩子都分到最多的弹珠的前提下,嫉妒值越大,分到弹珠的孩子越少;嫉妒值越小,分到弹珠的孩子越少。也就是说,这个嫉妒值是有单调性的。

那么可以二分答案。把原问题转换为:是否存在一种方案,使得嫉妒值 \(\leq mid\) 。

那么对于第 \(i\) 种数量为 \(a_i\) 的弹珠,它最少需要分给 \(\lceil \dfrac{a_i}{mid} \rceil\) 个孩子。把所有的弹珠需要的孩子数量相加,就能得到最少需要分给多少个孩子。

如果最终要分给的孩子数量 \(\leq n\) ,那么这个 \(mid\) 就是可行的,就应该缩小答案,也就是令二分边界 \(r=mid\) ,否则 \(mid\) 是不可行的,应该让 \(l=mid+1\) 。

时间复杂度 \(\mathcal{O}(m \log n)\) ,足已通过。

代码如下(不开 \(\texttt{long long}\) 见祖宗):

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <iostream>
#include <queue>
typedef long long LL;
using namespace std;
inline int read() {
    int num = 0 ,f = 1; char c = getchar();
    while (!isdigit(c)) f = c == '-' ? -1 : f ,c = getchar();
    while (isdigit(c)) num = (num << 1) + (num << 3) + (c ^ 48) ,c = getchar();
    return num * f;
}
const int N = 3e5 + 5 ,INF = 0x3f3f3f3f;
int a[N] ,n ,m;
inline bool check(LL mid) {
    LL ans = 0;
    for (int i = 1; i <= m; i++)
        ans += (a[i] - 1) / mid + 1; //上取整的优雅的写法。
    return ans <= n;
}
LL l = 1 ,r; //记得开 long long
signed main() {
    n = read(); m = read();
    for (int i = 1; i <= m; i++) a[i] = read() ,r = max(r ,(LL)a[i]);
    //注意 l 一定要 > 1 ,r 的话去所有弹珠数量的最大值就可以。因为最坏情况下是把所有同颜色的弹珠分给一个人。
    while (l < r) {
        LL mid = (l + r) >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    printf("%lld\n" ,l);
    return 0;
}
上一篇:「题解」合唱团 chorus


下一篇:字符串题目:检测大写字母