cf730 I. Olympiad in Programming and Sports

题意:

每个人有两种能力 a 和 b,在 \(tot\) 个人中选 n 个人到第一组,再另选 m 个人到第二组,最大化第一组中所有人的 a 之和加上第二组中所有人的 b 之和。

\(n\le 3000,n+m\le tot\)

思路:

把所有人按先按 a 从大到小再按 b 从大到小排序,以下讨论都基于排序后的数组

第一组中任何一个人的 a 必定大于任何一个没组的人的 a。否则,交换它们俩会更优

所以可以这样构造答案:

先把 \([1,n+i]\) 这些人都放进第一组

然后从第一组中拿 \(i\) 个人到第二组(它们只能去第二组,而不能没有组,否则违背之前的结论)。应该拿哪些呢?答案是拿 \([1,n+i]\) 中 b-a 最大的 \(i\)

这样第二组还差 \(m-i\) 个,所以把 \([n+i+1,tot]\) 中前 \(m-i\) 大的 b 放进第二组

上述几个 “xx区间中xx值前xx大的和” 可以用 set 之类的预处理出来,最后对 \(0\le i\le m\) 就能 \(O(1)\) 求答案了。但我这里放一种很慢(\(n^2logn\))但是比较短的写法:

先把 \([1,tot]\) 按先按 a 从大到小再按 b 从大到小排序;再把 \([1,n+i]\) 按 b-a 从小到大排序,最后把 \([n+i+1,tot]\) 按 b 从大到小排序。这样 \([1,n]\) 就是第一组,\([n+1,n+m]\) 就是第二组

const signed N = 3e3 + 3;
int tot, n, m;
struct node {
    int a, b, id;
} p[N];

int ans, ansi;
void cal(int I) {
    sort(p + 1, p + 1 + tot, [](node &x, node &y) {
         return x.a != y.a ? x.a > y.a : x.b > y.b;
    });
    sort(p + 1, p + 1 + n + I, [](node &x, node &y) {
         return x.b - x.a < y.b - y.a;
    });
    sort(p + n + I + 1, p + 1 + tot, [](node &x, node &y) {
         return x.b > y.b;
    });

    int res = 0; for(int i = 1; i <= n + m; i++)
        res += i <= n ? p[i].a : p[i].b;
    if(res > ans) ans = res, ansi = I;
}

signed main() {
    iofast;
    cin >> tot >> n >> m;
    for(int i = 1; i <= tot; i++) cin >> p[i].a, p[i].id = i;
    for(int i = 1; i <= tot; i++) cin >> p[i].b;

    for(int i = 0; i <= m; i++) cal(i);

    cout << ans << endl;
    cal(ansi); for(int i = 1; i <= n + m; i++)
        cout << p[i].id << " \n"[i==n];
}

posted @ 2022-04-22 14:13  Bellala  阅读(39)  评论(0)    收藏  举报