D - Optimal Partition
porb. :给一个数列a,长度为n,可以将a分成若干连续的串,当一个串的数字和为正时,贡献为len;为0,贡献为0;为负,贡献为-len;问在所有分法中,最大总贡献是多少
idea:考虑dp
dp[i] 以i结尾的若干连续串的最大总贡献
对应三种转移:
d
p
[
i
]
=
d
p
[
j
]
+
(
i
−
j
)
,
j
<
i
&
s
u
m
{
j
+
1
,
i
}
>
0
d
p
[
i
]
=
d
p
[
j
]
−
(
i
−
j
)
,
j
<
i
&
s
u
m
{
j
+
1
,
i
}
<
0
d
p
[
i
]
=
d
p
[
j
]
,
j
<
i
&
s
u
m
{
j
+
1
,
i
}
=
0
dp[i] = dp[j] + (i - j) \, , j < i \, \& \, sum\{j + 1, i\} > 0 \\ dp[i] = dp[j] - (i - j) \, , j < i \, \& \, sum\{j + 1, i\} < 0 \\ dp[i] = dp[j] \, , j < i \, \& \, sum\{j + 1, i\}= 0 \\
dp[i]=dp[j]+(i−j),j<i&sum{j+1,i}>0dp[i]=dp[j]−(i−j),j<i&sum{j+1,i}<0dp[i]=dp[j],j<i&sum{j+1,i}=0
考虑前缀和 + 等式移项,得
d
p
[
i
]
−
i
=
d
p
[
j
]
−
j
,
j
<
i
&
p
r
e
[
j
]
<
p
r
e
[
i
]
d
p
[
i
]
+
i
=
d
p
[
j
]
+
j
,
j
<
i
&
p
r
e
[
j
]
>
p
r
e
[
i
]
d
p
[
i
]
=
d
p
[
j
]
,
j
<
i
&
p
r
e
[
j
]
=
p
r
e
[
i
]
dp[i] - i = dp[j] - j\, , j < i \, \& \, pre[j] < pre[i] \\ dp[i] + i = dp[j] + j \, , j < i \, \& \,pre[j] > pre[i] \\ dp[i] = dp[j] \, , j < i \, \& \, pre[j] = pre[i] \\
dp[i]−i=dp[j]−j,j<i&pre[j]<pre[i]dp[i]+i=dp[j]+j,j<i&pre[j]>pre[i]dp[i]=dp[j],j<i&pre[j]=pre[i]
考虑三棵线段树分别维护三个值更新:Codeforces Round #783 (Div. 2) D题解 - 知乎 (zhihu.com)
感觉这样写很方便,参考了一下写法
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 5e5 + 100;
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll a[N];
struct node {
ll l, r, val;
};
ll p = 0;
node tr[3][N << 2];
void pushup(ll u) {
tr[p][u].val = max(tr[p][u << 1].val, tr[p][u << 1 | 1].val);
}
void build(ll u, ll l, ll r) {
if (l == r) {
tr[p][u] = {l, r, -inf};
} else {
tr[p][u] = {l, r, -inf};
ll mid = (l + r) >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(ll u, ll l, ll r, ll x) {
if (tr[p][u].l >= l && tr[p][u].r <= r) {
tr[p][u].val = max(tr[p][u].val, x);
return;
}
ll mid = (tr[p][u].l + tr[p][u].r) >> 1;
if (l <= mid) modify(u << 1, l, r, x);
if (r > mid) modify(u << 1 | 1, l, r, x);
pushup(u);
}
ll query(ll u, ll l, ll r) {
if (tr[p][u].l >= l && tr[p][u].r <= r) return tr[p][u].val;
ll mid = (tr[p][u].l + tr[p][u].r) >> 1;
ll res = -inf;
if (l <= mid) res = max(res, query(u << 1, l, r));
if (r > mid) res = max(res, query(u << 1 | 1, l, r));
return res;
}
ll pre[N], dp[N];
vector<ll> vec;
map<ll, ll> idxx;
ll n;
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
ll T;
cin >> T;
while (T--) {
vec.clear();
idxx.clear();
cin >> n;
for (ll i = 0; i <= n + 5; ++i) dp[i] = -inf;
dp[0] = 0;
for (ll i = 1; i <= n; ++i) {
cin >> a[i];
}
for (ll i = 1; i <= n; ++i) {
pre[i] = pre[i - 1] + a[i];
vec.push_back(pre[i]);
}
vec.push_back(pre[0]);
sort(vec.begin(), vec.end());
vec.erase(unique(vec.begin(), vec.end()), vec.end());
for (auto tt: vec) {
idxx[tt] = lower_bound(vec.begin(), vec.end(), tt) - vec.begin() + 1;
}
for (ll i = 0; i <= 2; ++i) {
p = i, build(1, 1, n + 5);
}
for (ll i = 0; i <= 2; ++i) {
ll pos = idxx[pre[0]];
p = i, modify(1, pos, pos, 0);
}
for (ll i = 1; i <= n; ++i) {
ll pos = idxx[pre[i]];
p = 0, dp[i] = max(dp[i], query(1, 1, pos - 1) + i);
p = 1, dp[i] = max(dp[i], query(1, pos, pos));
p = 2, dp[i] = max(dp[i], query(1, pos + 1, n + 1) - i);
p = 0, modify(1, pos, pos, dp[i] - i);
p = 1, modify(1, pos, pos, dp[i]);
p = 2, modify(1, pos, pos, dp[i] + i);
}
cout << dp[n] << endl;
}
return 0;
}