http://agc018.contest.atcoder.jp/tasks/agc018_c
解説
http://agc018.contest.atcoder.jp/submissions/1450483
解説放送の副読本的内容です。
Cを全て取る状態からスタートする。
すると、Aに変えるとA-Cだけ増加、Bに変えるとB-Cだけ増加するので、A-=C, B-=Cしておく。
ここで、A[i]-B[i]の昇順に並べておくと、前半からBをY個取る、後半からAをX個取るのが最適になる。
なぜ最適かは解説放送が詳しい。
AA,BBをA[i]-B[i]でソート済みのA,Bとする。
ここで、事前に以下のDPを用意しておく。
dp1[i] := 配列BBの[0,i]からY個取った和の最大値
dp2[i] ;= 配列AAの[i,N-1]からX個取った和の最大値
これを求めるにはpriority_queueを使うと良い。
関数dodp1の中身を少しだけ詳しく解説しておく。
c++のpriority_queueは大きい順から取り出す仕組みになっている。
今回は小さい順で取り出す目的で使いたいので、その場合はマイナスを付けて入れることで解決できる。
キューのサイズがY個を超えた場合は、先頭から取り出して、それを総和から引く。
これを繰り返すことで今までの最大Y個がキューの中に残り続け、その和をsmとして、逐次更新していく。
これで高速にdp更新ができる(dodp2関数も同様)。
あとは、境界線kについて全探索して、前半後半の和の最小値とCの総和を足せば答え
typedef long long ll; #define INF 1LL<<60 int X, Y, Z, N; ll A[101010], B[101010], C[101010]; ll AA[101010], BB[101010]; //--------------------------------------------------------------------------------------------------- ll dp1[101010]; void dodp1() { priority_queue<ll> que; rep(i, 0, N) dp1[i] = -INF; ll sm = 0; rep(i, 0, N) { que.push(-BB[i]); sm += BB[i]; if (Y < que.size()) { ll q = -que.top(); sm -= q; que.pop(); } if (que.size() == Y) dp1[i] = sm; } } //--------------------------------------------------------------------------------------------------- ll dp2[101010]; void dodp2() { priority_queue<ll> que; rep(i, 0, N) dp2[i] = -INF; ll sm = 0; rrep(i, N - 1, 0) { que.push(-AA[i]); sm += AA[i]; if (X < que.size()) { ll q = -que.top(); sm -= q; que.pop(); } if (que.size() == X) dp2[i] = sm; } } //--------------------------------------------------------------------------------------------------- void _main() { cin >> X >> Y >> Z; N = X + Y + Z; rep(i, 0, N) cin >> A[i] >> B[i] >> C[i]; ll ans = 0; rep(i, 0, N) { ans += C[i]; A[i] -= C[i]; B[i] -= C[i]; } vector<int> v; rep(i, 0, N) v.push_back(i); sort(v.begin(), v.end(), [&](int a, int b) { return A[a] - B[a] < A[b] - B[b]; }); rep(i, 0, N) { int j = v[i]; AA[i] = A[j]; BB[i] = B[j]; } dodp1(); dodp2(); ll ma = -INF; rep(k, 0, N - 1) ma = max(ma, dp1[k] + dp2[k + 1]); ans += ma; printf("%lld\n", ans); }