题目大意
给\(n\)个不大于\(k\)的数,让你尽可能的把它们分成较小的组,每组大于等于\(i\)的数不超过\(c[i]\)个。
分析
从最简单的情况开始考虑,对于\(n\)个数中最大的数\(i\),如果数组中一共有\(m_i\)个\(i\),那么这个数组中大于等于\(i\)的数的数量就是\(m_i\)个,所以说将这\(m_i\)个数分组至少需要\(\lceil {m_i/c[i]}\rceil\)组。如果是\(i-1\)呢,那么大于等于\(i-1\)的数的数量就是\(m_i+m_{i-1}\),需要分的组数至少需要\(\lceil {m_i+m_{i-1}/c[i-1]}\rceil\)组。等等,是不是忘了什么?我们前面分的组数和后面不一定是相等的,所以要想满足每个条件,肯定要取最大的组数。我们知道了组数了具体该怎么分呢?经过前面的计算我们已经得到了最小可行的组数,所以我们只要按顺序把各个数均摊到我们分的组里就行了。
具体实现
我们可以先用一个桶来计数,算出每个数字出现的次数,然后倒着求后缀和,就能得到大于当前数字的数量,后面求最大分组就很容易了(记得向上取整)。后面的分组可以用模运算。
代码
const int maxn = 2e5+10;
vector<int> ans[maxn];
int cnt[maxn], c[maxn];
int main(void) {
int n, k;
scanf("%d%d", &n, &k);
for (int i = 0, num; i<n; ++i) {
scanf("%d", &num);
++cnt[num];
}
for (int i = 1; i<=k; ++i) scanf("%d", &c[i]);
int sum = 0, maxx = -1;
for (int i = k; i>=1; --i) {
sum += cnt[i+1];
//cout << sum << endl;
maxx = max(maxx, (sum+c[i]-1)/c[i]);
}
int tot = 0;
for (int i = k; i>=1; --i)
while(cnt[i]) {
ans[tot%maxx].push_back(i);
--cnt[i];
++tot;
}
printf("%d\n", maxx);
for (int i = 0; i<maxx; ++i)
if (!ans[i].empty()) {
printf("%d", (int)ans[i].size());
for (auto num : ans[i]) printf(" %d", num);
putchar(endl);
}
return 0;
}