[CF1442D] Sum
[题目链接]
https://codeforces.com/contest/1442/problem/D
[题解]
首先一个结论是 : 必然存在一种最优解 , 使得至多有一个数组没有被全取。
证明 : 考虑有两个数组没有被全取 , 最后一个被取的元素分别是 \(t1\) 和 \(t2\) , 若 \(a_{t1} \leq b_{t2}\) , 那么把 \(a\) 换成 \(b\) 必然更优。
因此问题转化为了一个 "缺一背包" 模型 , 不妨用 \(solve(l , r)\) 这个函数表示除了 \([l , r]\) 这段区间的背包 , 那么分治即可。
时间复杂度 : \(O(NMlogN)\)
[代码]
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i , l , r) for (int i = (l); i < (r); ++i)
const int MN = 3005;
int N , K;
LL ans = 0 , f[MN];
vector < LL > a[MN];
inline void chkmax(LL &x , LL y) {
x = max(x , y);
}
inline void upd(int v , LL w) {
for (int j = K; j >= v; --j) chkmax(f[j] , f[j - v] + w);
}
inline void solve(int l , int r) {
if (l == r) {
for (int i = 0; i < a[l].size() && i <= K; ++i) chkmax(ans , f[K - i] + a[l][i]);
return;
}
int mid = l + r >> 1; vector < LL > tmp;
for (int i = 0; i <= K; ++i) tmp.emplace_back(f[i]);
for (int i = mid + 1; i <= r; ++i) upd(a[i].size() - 1 , a[i][a[i].size() - 1]);
solve(l , mid);
for (int i = 0; i <= K; ++i) f[i] = tmp[i];
for (int i = l; i <= mid; ++i) upd(a[i].size() - 1 , a[i][a[i].size() - 1]);
solve(mid + 1 , r);
for (int i = 0; i <= K; ++i) f[i] = tmp[i];
}
int main() {
scanf("%d%d" , &N , &K);
for (int i = 1; i <= N; ++i) {
int m; scanf("%d" , &m);
a[i].emplace_back(0LL);
for (int j = 1; j <= m; ++j) {
int x; scanf("%d" , &x);
a[i].emplace_back(a[i][j - 1] + (LL) x);
}
}
solve(1 , N);
printf("%lld\n" , ans);
return 0;
}