cf730 I. Olympiad in Programming and Sports
阿新 • • 發佈:2022-04-22
題意:
每個人有兩種能力 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]\)
這樣第二組還差 \(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]; }