[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;
}
posted @ 2020-11-22 18:57  evenbao  阅读(121)  评论(0编辑  收藏  举报