bzoj3636. 教义问答手册
bzoj3636. 教义问答手册
容易想到一个暴力的DP做法,设 \(f_{l,r}\) 表示区间 \([l,r]\) 的答案,则有
\[f_{l,r}=\max\left\{f_{l,r-L}+\sum\limits_{r-L<k\le r}a_k,\,f_{l,r-1}\right\}
\]
使用前缀和优化便是 \(\mathcal O(NQ)\) 的,显然过不去。注意到这个DP在 \(l\) 或 \(r\) 固定时是 \(\mathcal O(N)\) 的。
这里我们考虑使用分治离线询问优化暴力。假设当前分治区间为 \([l,r]\);若某个询问 \((x,y)\) 满足 \(l\le x\le m\and m<y\le r\) 其中 \(m=\left\lfloor\frac{l+r}{2}\right\rfloor\),那么就把询问 \((x,y)\) 挂在分治区间 \([l,r]\) 下。
分别预处理出满足 \(m-K<r\le m\) 和 \(m<l\le m+K\) 处的DP值。考虑询问 \((x,y)\) 的决策:若不存在一个连续段跨越 \(m\),则贡献为左右两边子区间DP值相加;若存在一个连续段跨越 \(m\),由于连续段的长度不超过 \(K\),这样的连续段最多有 \(K\) 种,将它们枚举出来,贡献为该连续段的贡献加上将 \([l,r]\) 除去这个连续段后剩余的两个子区间的DP值之和。于是,通过一些预处理,查询可以做到 \(\mathcal O(K)\)。
对于挂在同一个分治区间下的询问一起处理,这样每一层内只需要一次预处理。分治层内预处理的时间复杂度是 \(\mathcal O(NK)\),单次查询的复杂度为 \(\mathcal O(K)\),总时间复杂度就是 \(\mathcal O(NK\log N)\)。
参考代码
#include <bits/stdc++.h>
using namespace std;
template<typename _Tp> _Tp &min_eq(_Tp &x, const _Tp &y) { return x = min(x, y); }
template<typename _Tp> _Tp &max_eq(_Tp &x, const _Tp &y) { return x = max(x, y); }
static constexpr int Maxn = 1e5 + 5, MaxL = 55, MaxQ = 1e5 + 5;
int n, Q, L, ans[MaxQ], a[Maxn], s[Maxn + MaxL], sl[Maxn], sr[Maxn];
struct query { int l, r, i; } q[MaxQ], q1[MaxQ], q2[MaxQ];
void calcl(int *f, int l, int r) {
for (int i = l - 1; i <= r && i < l + L - 1; ++i) f[i] = 0;
for (int i = l + L - 1; i <= r; ++i) f[i] = max(f[i - 1], f[i - L] + sr[i]);
} // calcl
void calcr(int *f, int l, int r) {
for (int i = r + 1; i >= l && i > r - L + 1; --i) f[i] = 0;
for (int i = r - L + 1; i >= l; --i) f[i] = max(f[i + 1], f[i + L] + sl[i]);
} // calcr
void solve(int l, int r, int ql, int qr) {
if (ql > qr || l + L - 1 > r) return ;
int mid = (l + r) / 2, qn1 = 0, qn2 = 0;
static int fl[MaxL][Maxn], fr[MaxL][Maxn];
for (int i = mid - 0; i >= l && mid - i + 1 <= L; --i) calcr(fl[mid - i + 0], l, i);
for (int i = mid + 1; i <= r && i - mid + 0 <= L; ++i) calcl(fr[i - mid - 1], i, r);
for (int qi = ql; qi <= qr; ++qi) {
auto [pl, pr, pi] = q[qi];
if (pr <= mid) q1[++qn1] = q[qi];
else if (pl > mid) q2[++qn2] = q[qi];
else {
max_eq(ans[pi], fl[0][pl] + fr[0][pr]);
for (int ri = 1, li; ri < L; ++ri) {
if (mid - pl + 1 < (li = L - ri) || pr - mid < ri) continue;
max_eq(ans[pi], fl[li][pl] + fr[ri][pr] + sr[mid + ri]);
}
}
}
for (int i = 1; i <= qn1; ++i) q[ql + i - 1] = q1[i];
for (int i = 1; i <= qn2; ++i) q[qr - i + 1] = q2[i];
solve(l, mid, ql, ql + qn1 - 1);
solve(mid + 1, r, qr - qn2 + 1, qr);
} // solve
int main(void) {
scanf("%d%d", &n, &L); s[0] = sr[0] = 0;
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), s[i] = s[i - 1] + a[i];
for (int i = n + 1; i <= n + L; ++i) s[i] = s[i - 1];
for (int i = 1; i <= n; ++i) sr[i] = (i < L ? s[i] : s[i] - s[i - L]);
for (int i = 1; i <= n; ++i) sl[i] = s[i + L - 1] - s[i - 1];
scanf("%d", &Q);
for (int i = 1; i <= Q; ++i) scanf("%d%d", &q[i].l, &q[i].r), q[i].i = i;
memset(ans, 0, sizeof(ans)); solve(1, n, 1, Q);
for (int i = 1; i <= Q; ++i) printf("%d\n", ans[i]);
exit(EXIT_SUCCESS);
} // main
浙公网安备 33010602011771号