cf1342 D. Multiple Testcases(思维,贪心)

题意:

\(n\) 个数分成尽量少的 \(t\) 组。每组中大于等于 \(i\) 的数不能超过 \(c_i\) 个。输出 \(t\) 和一种分组方案。

1 <= ai <= k

思路:

注意 \(c_i\) 之间是包含关系,相当于每个值的容量的后缀和。 \(c_1\ge c_2\ge\cdots\)

因为题目给的是大于等于 \(i\) 的个数限制,所以考虑先放较大的数。

\(cnt[x]\) 表示原数组中 \(x\) 出现的次数。先放原数组中最大的数 \(maxx\) ,那么 \(t\) 需满足 \(t*c[maxx]\ge cnt[maxx]\)。然后放第二大的数 \(maxx-1\),要求 \(t*c[maxx-1]\ge cnt[maxx]+cnt[maxx-1]\)。然后放第三大的数 \(maxx-2\),要求 \(t*c[maxx-2]\ge cnt[maxx]+cnt[maxx-1]+cnt[maxx-2]\)

以上所有不等式须同时满足。如果把 \(cnt[]\) 变成后缀和,就是 \(t*c_x\ge cnt_x\) 对定义域内所有 \(x\) 成立。所以 \(t=max\{\lceil cnt_x/c_x \rceil\}\)

接下来从大到小放数,第 \(1\)\(t\) 组每次放一个,然后再在第 \(1\)\(t\) 组每组放一个,放完为止,这样即可保证大的数先被满足。实测从小到大放数也可以。

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, k, a[N], c[N], cnt[N];
vector<int> ans[N];
signed main()
{
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]), cnt[a[i]]++;
    for(int i = 1; i <= k; i++) scanf("%d", &c[i]);

    for(int i = k-1; i >= 0; i--) cnt[i] += cnt[i+1]; //后缀和

    int t = 1;
    for(int x = 1; x <= k; x++) t = max(t, (int)ceil(1.0*cnt[x]/c[x]));
    printf("%d\n", t);

    sort(a + 1, a + 1 + n, greater<int>());
    for(int i = 1; i <= n; i++) ans[i%t].push_back(a[i]);
    
    for(int i = 0; i < t; i++) //输出方案
    {
        printf("%d ", ans[i].size());
        for(int j : ans[i]) printf("%d ", j);
        puts("");
    }

    return 0;
}

posted @ 2021-12-25 21:43  Bellala  阅读(29)  评论(0)    收藏  举报