- 如果 m = 1 m=1 m=1 我们只需要求最大子段和即可。
- 但是当 m > 1 m>1 m>1 时,我们的选取可能会产生后效性。
- 比如说黄色部分是选取一个子段的最优方案,橙色部分是选取两个子段的最优答案。
- 那么我们需要搞一个资瓷删除部分已经选过的数的操作。
- 我们可以考虑直接把选出的部分取反,即区间取反操作。
- 这里利用了类似网络流的退流思想。
- 然后每次需要动态维护最大子段和。
- 线段树实现即可。
- 注意这里要求至多选取 m m m 个子段,若当前的最大子段和小于 0 0 0,那么接下来选取肯定不优,直接
break
。
- 时间复杂度 O ( m log n ) O(m\log n) O(mlogn)。
#include
template <class T>
inline void read(T &x)
{
static char ch;
static bool opt;
while (!isdigit(ch = getchar()) && ch != '-');
x = (opt = ch == '-') ? 0 : ch - '0';
while (isdigit(ch = getchar()))
x = x * 10 + ch - '0';
if (opt) x = ~x + 1;
}
#define lc_info x << 1, l, mid
#define rc_info x << 1 | 1, mid + 1, r
const int MaxN = 1e5 + 5;
const int MaxS = MaxN << 2;
const int INF = 0x7fffffff;
struct segment
{
int l, r, s;
segment(){}
segment(int a, int b, int c):
l(a), r(b), s(c) {}
inline bool operator < (const segment &rhs) const
{
return s < rhs.s;
}
inline segment operator + (const segment &rhs) const
{
return segment(l, rhs.r, s + rhs.s);
}
};
template <class T>
inline void relax(T &x, const T &y)
{
if (x < y) x = y;
}
template <class T>
inline void tense(T &x, const T &y)
{
if (y < x) x = y;
}
int n, m;
int a[MaxN], lef[MaxS], rit[MaxS];
int sum[MaxS];
bool tag[MaxS];
segment min_pre[MaxS], min_suf[MaxS], min_val[MaxS];
segment max_pre[MaxS], max_suf[MaxS], max_val[MaxS];
inline void neg(int &x)
{
x = ~x + 1;
}
inline void upt(int x)
{
int mid = lef[x] + rit[x] >> 1;
sum[x] = sum[x << 1] + sum[x << 1 | 1];
segment lc_seg = segment(lef[x], mid, sum[x << 1]);
segment rc_seg = segment(mid + 1, rit[x], sum[x << 1 | 1]);
max_pre[x] = max_suf[x] = max_val[x] = segment(0, 0, -INF);
min_pre[x] = min_suf[x] = min_val[x] = segment(0, 0, INF);
relax(max_pre[x], max_pre[x << 1]);
relax(max_pre[x], lc_seg + max_pre[x << 1 | 1]);
relax(max_suf[x], max_suf[x << 1 | 1]);
relax(max_suf[x], max_suf[x << 1] + rc_seg);
relax(max_val[x], std::max(max_val[x << 1], max_val[x << 1 | 1]));
relax(max_val[x], max_suf[x << 1] + max_pre[x << 1 | 1]);
tense(min_pre[x], min_pre[x << 1]);
tense(min_pre[x], lc_seg + min_pre[x << 1 | 1]);
tense(min_suf[x], min_suf[x << 1 | 1]);
tense(min_suf[x], min_suf[x << 1] + rc_seg);
tense(min_val[x], std::min(min_val[x << 1], min_val[x << 1 | 1]));
tense(min_val[x], min_suf[x << 1] + min_pre[x << 1 | 1]);
}
inline void inv(int x)
{
neg(sum[x]);
neg(max_pre[x].s), neg(min_pre[x].s), std::swap(max_pre[x], min_pre[x]);
neg(max_suf[x].s), neg(min_suf[x].s), std::swap(max_suf[x], min_suf[x]);
neg(max_val[x].s), neg(min_val[x].s), std::swap(max_val[x], min_val[x]);
tag[x] ^= 1;
}
inline void dnt(int x)
{
if (tag[x])
{
inv(x << 1);
inv(x << 1 | 1);
tag[x] = 0;
}
}
inline void build(int x, int l, int r)
{
lef[x] = l, rit[x] = r;
max_pre[x] = max_suf[x] = max_val[x] = segment(l, r, -INF);
min_pre[x] = min_suf[x] = min_val[x] = segment(l, r, INF);
if (l == r)
{
sum[x] = a[l];
max_pre[x] = max_suf[x] = max_val[x] = segment(l, r, a[l]);
min_pre[x] = min_suf[x] = min_val[x] = segment(l, r, a[l]);
return;
}
int mid = l + r >> 1;
build(lc_info);
build(rc_info);
upt(x);
}
inline void modify(int x, int l, int r, int u, int v)
{
if (u <= l && r <= v)
return (void)(inv(x));
dnt(x);
int mid = l + r >> 1;
if (u <= mid)
modify(lc_info, u, v);
if (v > mid)
modify(rc_info, u, v);
upt(x);
}
int main()
{
read(n), read(m);
for (int i = 1; i <= n; ++i)
read(a[i]);
build(1, 1, n);
int ans = 0;
for (int i = 1; i <= m; ++i)
{
segment res = max_val[1];
if (res.s < 0) break;
ans += res.s;
modify(1, 1, n, res.l, res.r);
}
printf("%d\n", ans);
return 0;
}