题解:AT_abc383_g [ABC383G] Bar Cover
posted on 2024-12-26 10:13:33 | under | source
记 \(b_i=\sum\limits_{j=i}^{i+k-1} a_j\),即 \(i\) 为起点的块的价值。
考虑分治,枚举跨过中点的块,于是有状态 \(f_{l,r,i,j,s}\) 表示 \([l,r]\) 区间中放了 \(s\) 个块,且开始 \(i\) 个、结尾 \(j\) 个位置强制不放块,最大权值是多少。
枚举中间块占据左边区间 \(p\) 个位置,左部区间有 \(c\) 个块,则有转移:
\[f_{l,r,i,j,s}\gets f_{l,mid,i,p,c}+f_{mid+1,r,k-p,j,s-1-c}+b_{r-p+1}
\]
对于恰好放多少块的问题,大胆猜测是个凸包,打个表发现确实如此。
所以转移可以视为凸包的合并,由于凸包斜率单调,不难发现转移具有决策单调性,即 \(s+1\) 的最优决策点 \(c_1\) 与 \(s\) 的决策点 \(c_0\),一定满足 \(c_1\ge c_0\)。所以可以做到线性合并。从中可以感性理解出确实是凸包。
对于大小 \(<2k\) 的区间,直接计算出 \(f\) 即可。于是分治树叶子数量为 \(\frac nk\),总复杂度 \(O(nk^2\log \frac nk)\)。
其实看出凸包就可以用 slope trick 解决无需分治,复杂度会更好。但是此题 \(k\) 过小,有种大炮打蚊子的感觉。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 5, K = 5, inf = 1e15;
int n, k, a[N], b[N];
vector<int>f[N << 2][K][K];
inline void mer(const vector<int> &A, const vector<int> &B, vector<int> &C, int add){
bool fl = (add != inf);
int na = A.size() - 1, nb = B.size() - 1, nc = na + nb;
for(int k = 0, st = 0, ed; k <= nc; ++k){
ed = k - st;
if(ed > nb) ed = nb, st = k - ed;
while(st < na && ed > 0 && A[st] + B[ed] < A[st + 1] + B[ed - 1]) ++st, --ed;
C[k + fl] = max(C[k + fl], A[st] + B[ed] + fl * add);
}
}
inline void calc(int u, int l, int r){
int len = r - l + 1;
for(int i = 0; i < k; ++i)
for(int j = 0; j < k && i + j <= len; ++j)
f[u][i][j] = vector<int> (1 + (len - i - j) / k, -inf);
if(len < 2 * k){
for(int i = 0; i < k; ++i)
for(int j = 0; j < k && i + j <= len; ++j){
f[u][i][j][0] = 0;
for(int p = l + i; p + k - 1 < r - j + 1; ++p) f[u][i][j][1] = max(f[u][i][j][1], b[p]);
}
return ;
}
int mid = (l + r) >> 1, ls = (u << 1), rs = (u << 1) | 1;
calc(ls, l, mid), calc(rs, mid + 1, r);
for(int i = 0; i < k && i <= mid - l + 1; ++i)
for(int j = 0; j < k && j <= r - mid; ++j){
mer(f[ls][i][0], f[rs][0][j], f[u][i][j], inf);
for(int p = 1; p < k; ++p)
if(i + p <= mid - l + 1 && j + k - p <= r - mid)
mer(f[ls][i][p], f[rs][k - p][j], f[u][i][j], b[mid - p + 1]);
}
}
signed main(){
cin >> n >> k;
for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]), a[i] += a[i - 1];
for(int i = 1; i <= n - k + 1; ++i) b[i] = a[i + k - 1] - a[i - 1];
calc(1, 1, n);
for(int i = 1; i < f[1][0][0].size(); ++i) printf("%lld ", f[1][0][0][i]);
return 0;
}

浙公网安备 33010602011771号