Loading

【题解】CF1442D Sum

传送门

题意

给定 \(n\)不降的数组。有一个值 \(ans\),初始为 \(0\)。你需要进行如下操作 \(k\) 次:

选择一个数组,把 \(ans\) 加上数组的第一个元素,之后把它删除。

请求出 \(ans\) 最大是多少。

所有元素总个数 \(\leq 10^6 \space n,k\leq 3000 \space 0 \leq a_{i,j} \leq 10^8\)

题解

显然有一个结论

最多只有一个组会取一部分,其他组要么一个都不取,要么都取完

比较显然,感性理解一下,因为是不降的,如果有两个没取完,那么显然取完其中的一个会更优

然后如果枚举没取完的,那么两边背包合并还是 \(O(nk^2)\),相当于没有优化

考虑分治 \(solve(l,r)\) 代表没取完的在 \([l,r]\) 中,所以 \([1,l)\)\((r,n]\) 都是可以直接背包的

调用 \(solve(l,mid)\) 时就要把 \(mid+1~r\) 的加到背包,\(solve(mid+1,r)\) 同理

记得要回滚

Code
#include <bits/stdc++.h>
using namespace std;
#define FOR(i,j,k) for(int i=j; i<=k; ++i)
#define ROF(i,j,k) for(int i=j; i>=k; --i)
inline int read (void) {
  int x = 0, f = 1, ch = getchar();
  while(!isdigit(ch)) { if(ch == '-') f = -f; ch = getchar(); }
  while(isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }
  return x * f;
}
const int maxn = 3005;
vector <int> v[maxn];
long long ans, tot[maxn], f[maxn];
long long now, sta[maxn*maxn*2][2];
int k, sz[maxn];
inline void roll_back (int flag) {
  while(now > flag) {
    f[sta[now][0]] = sta[now][1];
    -- now;
  }
}
void solve (int l, int r) {
  if(l == r) {
    long long sum = 0;
    ans = max(ans, f[k]);
    FOR(i,0,sz[l]-1) if(i < k) ans = max(ans, (sum += v[l][i]) + f[k - i - 1]);
    return ;
  }
  int flag = now, mid = (l + r >> 1);
  FOR(i,mid+1,r)
    ROF(j,k,sz[i])
      if(f[j] < f[j - sz[i]] + tot[i]) {
        sta[++ now][0] = j;
        sta[now][1] = f[j];
        f[j] = f[j - sz[i]] + tot[i];
      }
  solve (l, mid);
  roll_back (flag);
  FOR(i,l,mid)
    ROF(j,k,sz[i])
      if(f[j] < f[j - sz[i]] + tot[i]) {
        sta[++ now][0] = j;
        sta[now][1] = f[j];
        f[j] = f[j - sz[i]] + tot[i];
      }
  solve (mid+1, r);
  roll_back (flag);
}
int main (void) {
  int n = read(); k = read();
  FOR(i,1,n) {
    sz[i] = read();
    FOR(j,1,sz[i]) {
      int x = read();
      tot[i] += x;
      v[i].push_back(x);
    }
  }
  solve (1, n);
  printf("%lld\n", ans);
  return 0;
}
posted @ 2022-01-21 23:37  静谧时空  阅读(62)  评论(0)    收藏  举报