Codeforces 1442D Sum
https://codeforces.ml/contest/1442/problem/D
题意:给 \(n\) 个不减的正数数组,每个数组只能从头开始取,求任取 \(k\) 个数和的最大值。 \(1 \le n, k \le 3\,000\)
题解:
显然没有递减的条件的话就是个条件依赖背包问题,然后 \(O(n^2k)\) 就写完了。
然后发现有这个条件之后,最后的答案中一定最多有一个序列是没有取满的,可以反证法证明。
对于序列 i 和 j 如果都没有取满,\(next_i\) 表示 i 序列中下一个数,\(next_j\) 表示 j 序列中下一个数,则 \(now_j \le next_i\) 且 \(now_i \le next_j\) 都成立,显然不是最优解。
然后就想到把同一个组看成一个物品添加到背包中,维护一下前后缀的背包值然后合并,求中间去掉一个的答案,但是由于背包合并的复杂度是 \(O(k^2)\) 显然不成立。
有个科技叫:"knapsack of all elements but one"
然后就分治一下,在分治的最底层(只有一个元素时)求解,前面的就把组内的元素看成一个物品添加至背包。
算一下复杂度:分治中某个地方有 len 个数组,则需要把 len 个物品添加进背包,复杂度为 $ len*k $ 由于分治树中有 logn 层,每层的 len 和都是 n,所以总复杂度就是 \(O(nklogn)\)
总体来说就是为了避免背包中删除一个物品这种过于复杂的操作。
/*================================================================
*
* 创 建 者: badcw
* 创建日期: 2021/3/11 8:44 下午
*
================================================================*/
#include <bits/stdc++.h>
#define VI vector<int>
#define ll long long
#define dbg1(x) cerr<<#x<<"="<<(x)<<" "
#define dbg2(x) cerr<<#x<<"="<<(x)<<"\n"
using namespace std;
namespace IO {
template<class T>
void _R(T &x) { cin >> x; }
void _R(int &x) { scanf("%d", &x); }
void _R(ll &x) { scanf("%lld", &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { x = getchar(); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template<class T, class... U>
void R(T &head, U &... tail) {_R(head),R(tail...);}
template<class T>
void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const ll &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template<class T, class U>
void _W(const pair<T, U> &x) {_W(x.first),putchar(' '),_W(x.second);}
template<class T>
void _W(const vector<T> &x) { for (auto i = x.begin(); i != x.end(); _W(*i++)) if (i != x.cbegin()) putchar(' '); }
void W() {}
template<class T, class... U>
void W(const T &head, const U &... tail) {_W(head),putchar(sizeof...(tail) ? ' ' : '\n'),W(tail...);}
}
using namespace IO;
const int maxn = 3005;
const int mod = 1e9+7;
ll qp(ll a, ll n) {
ll res = 1;
n %= mod - 1;
if (n < 0) n += mod - 1;
while (n > 0) {
if (n & 1) res = res * a % mod;
a = a * a % mod;
n >>= 1;
}
return res;
}
ll qp(ll a, ll n, int mod) {
ll res = 1;
n %= mod - 1;
if (n < 0) n += mod - 1;
while (n > 0) {
if (n & 1) res = res * a % mod;
a = a * a % mod;
n >>= 1;
}
return res;
}
vector<VI> item;
int n, k;
ll f[maxn];
pair<int, ll> a[maxn];
ll res;
inline void dfs(int l, int r) {
if (l > r) return;
if (l == r) {
ll sum = 0;
for (int i = 0; i < (int)item[l].size(); ++i) {
sum += item[l][i];
res = max(res, f[k - i - 1] + sum);
}
} else {
ll tmp[maxn]; for (int i = 0; i <= k; ++i) tmp[i] = f[i];
int m = l + r >> 1;
for (int i = m + 1; i <= r; ++i) {
for (int j = k; j >= a[i].first; --j) {
f[j] = max(f[j], f[j - a[i].first] + a[i].second);
}
}
dfs(l, m);
for (int i = 0; i <= k; ++i) f[i] = tmp[i];
for (int i = l; i <= m; ++i) {
for (int j = k; j >= a[i].first; --j) {
f[j] = max(f[j], f[j - a[i].first] + a[i].second);
}
}
dfs(m + 1, r);
}
}
int main(int argc, char* argv[]) {
R(n, k); item.resize(n + 1);
for (int i = 1; i <= n; ++i) {
ll sum = 0;
int sz; R(sz);
item[i].resize(sz);
for (int j = 0; j < sz; ++j) {
R(item[i][j]);
sum += item[i][j];
}
if ((int)item[i].size() > k) item[i].resize(k);
a[i] = {sz, sum};
}
dfs(1, n);
W(res);
return 0;
}