1. 程式人生 > 其它 >cf730 I. Olympiad in Programming and Sports

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];
}