前言:
麻了,这都想不到。
复课后整个人搞 OI 时的专注和认真的态度都少了很多,可能是 csp 和 noip 过了,没有考试了,或是题停课时考试成绩有所提高,得意忘形了。今后必须要严格按照做题技巧上面来,认真总结做题技巧,积累套路,再糊弄就要退役了。
描述:
给定 \(n\) 个不降的数组。有一个值 \(ans\),初始为 \(0\)。你需要进行如下操作 \(k\) 次:
选择一个数组,把 \(ans\) 加上数组的第一个元素,之后把它删除。
请求出 \(ans\) 最大是多少。
所有数组的元素总个数 \(\leq 10^6\),\(n,k\leq 3000\)。
思路:
考虑从特殊情况推广到一般情况。对于 \(n=2\) ,发现最优解不可能出现两个序列都取了但没取完的情况。
因为序列 不降 ,假如 \(a_i>b_i\) ,那么可以加入 \(a_{i+1}\) ,删除 \(b_i\) ,反之同理。
考虑将 \(n=2\) 推广到一般情况,感性证明可以发现无论何时,顶多只有 \(1\) 个序列取了但没取完。
考虑分治,分治到 \(l=r\) 的情况时更新答案。
代码:
#include <bits/stdc++.h>
#define LL long long
#define DB double
#define PR pair <int, int>
#define MK make_pair
#define pb push_back
#define fi first
#define se second
#define RI register int
#define Low(x) (x & (-x))
using namespace std;
const int kN = 3e5 + 5;
int n, K, num[kN]; LL val[kN], ans;
vector <int> a[kN];
vector <LL> f;
void Solve(int l, int r) {
if (l == r) {
LL sum = 0;
for (int i = 0; i < num[l]; ++i) {
sum += a[l][i];
ans = max(ans, f[K - (i + 1)] + sum);
if (i + 1 >= K) break;
}
return;
}
int mid = (l + r) >> 1;
vector <LL> tmp = f;
for (int i = l; i <= mid; ++i)
for (int j = K; j >= num[i]; --j)
f[j] = max(f[j], f[j - num[i]] + val[i]);
Solve(mid + 1, r);
f = tmp;
for (int i = mid + 1; i <= r; ++i)
for (int j = K; j >= num[i]; --j)
f[j] = max(f[j], f[j - num[i]] + val[i]);
Solve(l, mid);
}
signed main() {
scanf("%d%d", &n, &K);
for (int i = 1; i <= n; ++i) {
scanf("%d", &num[i]);
for (int j = 0; j < num[i]; ++j) {
a[i].pb(0), scanf("%d", &a[i][j]), val[i] += a[i][j];
}
}
for (int i = 1; i <= K; ++i) f.pb(0);
Solve(1, n);
printf("%lld\n", ans);
return 0;
}