知识点:线段树,分治
这个题有三个标尺,除了维护最大子列和之外,还要维护最小左边界最小右边界,所以我们之前维护的数据之外,还要再加上四个,那就是当前区间满足条件的最大子列和的左右边界,和最大前缀的右边界,最大后缀的左边界,然后我们通过子问题合成原问题的时候,需要分类讨论,来修改这8个变量,因为右子区间的最大子列和的左边界肯定是最大的,那么只有当右子区间的最大子列和比中间和左子区间的都要大的时候,那么才用它当成答案,这样只剩下中间和左子区间的了,再分类讨论就行了,这个题除了线段树,分治思想之外,就是分类讨论了,并且加深了线段树写法的印象,中间有两处代码是一模一样的,可以合并成一个函数的,但是这样子写了之后时间变慢了,应该是传递参数的原因吧
没有合并成一个的写法,可以发现程序里面两处的逻辑是完全一样的,这个对应分治算法是属于第三步骤,我们向下递归,求得子问题的解,然后通过两个子问题的解合成原问题的解
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 5e5 + 5;
struct segt {
int l, r, L, R, l1, r1;
i64 Max, sum, lmax, rmax;
} t[N * 4];
int n, m, a[N];
void pushup(int p) {
int p1 = p * 2, p2 = p * 2 + 1;
t[p].sum = t[p1].sum + t[p2].sum;
t[p].lmax = max(t[p1].lmax, t[p1].sum + t[p2].lmax);
t[p].rmax = max(t[p2].rmax, t[p2].sum + t[p1].rmax);
t[p].l1 = (t[p1].sum + t[p2].lmax > t[p1].lmax ? t[p2].l1 : t[p1].l1);
t[p].r1 = (t[p2].rmax > t[p2].sum + t[p1].rmax ? t[p2].r1 : t[p1].r1);
if (t[p2].Max > max(t[p1].Max, t[p1].rmax + t[p2].lmax)) {
t[p].Max = t[p2].Max;
t[p].L = t[p2].L;
t[p].R = t[p2].R;
} else {
i64 midmax = t[p1].rmax + t[p2].lmax;
if (t[p1].Max > midmax || (t[p1].Max == midmax && t[p1].L <= t[p1].r1)) {
t[p].Max = t[p1].Max;
t[p].L = t[p1].L;
t[p].R = t[p1].R;
} else {
t[p].Max = midmax;
t[p].L = t[p1].r1;
t[p].R = t[p2].l1;
}
}
}
void build(int p, int l, int r) {
t[p].l = l; t[p].r = r;
if (l == r) {
t[p].Max = t[p].lmax = t[p].rmax = t[p].sum = a[l];
t[p].L = t[p].R = t[p].l1 = t[p].r1 = l;
return;
}
int mid = (l + r) / 2;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
pushup(p);
}
segt ask(int p, int l, int r) {
if (l <= t[p].l && t[p].r <= r) return t[p];
int mid = (t[p].l + t[p].r) / 2;
if (r <= mid) return ask(p * 2, l, r);
if (l > mid) return ask(p * 2 + 1, l, r);
segt cur, t1 = ask(p * 2, l, r), t2 = ask(p * 2 + 1, l, r);
cur.sum = t1.sum + t2.sum;
cur.lmax = max(t1.lmax, t1.sum + t2.lmax);
cur.rmax = max(t2.rmax, t2.sum + t1.rmax);
cur.l1 = (t1.sum + t2.lmax > t1.lmax ? t2.l1 : t1.l1);
cur.r1 = (t2.rmax > t2.sum + t1.rmax ? t2.r1 : t1.r1);
if (t2.Max > max(t1.Max, t1.rmax + t2.lmax)) {
cur.Max = t2.Max;
cur.L = t2.L;
cur.R = t2.R;
} else {
i64 midmax = t1.rmax + t2.lmax;
if (t1.Max > midmax || (t1.Max == midmax && t1.L <= t1.r1)) {
cur.Max = t1.Max;
cur.L = t1.L;
cur.R = t1.R;
} else {
cur.Max = midmax;
cur.L = t1.r1;
cur.R = t2.l1;
}
}
return cur;
}
int main() {
int tt = 1;
while (scanf("%d%d", &n, &m) != EOF) {
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
build(1, 1, n);
printf("Case %d:\n", tt++);
while (m--) {
int l, r;
scanf("%d%d", &l, &r);
segt ans = ask(1, l, r);
printf("%d %d\n", ans.L, ans.R);
}
}
return 0;
}
合成一个的,时间变慢了两倍多
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 5e5 + 5;
struct segt {
int l, r, L, R, l1, r1;
i64 Max, sum, lmax, rmax;
} t[N * 4];
int n, m, a[N];
segt pushup(segt t1, segt t2) {
segt cur;
cur.l = t1.l; cur.r = t2.r;
cur.sum = t1.sum + t2.sum;
cur.lmax = max(t1.lmax, t1.sum + t2.lmax);
cur.rmax = max(t2.rmax, t2.sum + t1.rmax);
cur.l1 = (t1.sum + t2.lmax > t1.lmax ? t2.l1 : t1.l1);
cur.r1 = (t2.rmax > t2.sum + t1.rmax ? t2.r1 : t1.r1);
if (t2.Max > max(t1.Max, t1.rmax + t2.lmax)) {
cur.Max = t2.Max;
cur.L = t2.L;
cur.R = t2.R;
} else {
i64 midmax = t1.rmax + t2.lmax;
if (t1.Max > midmax || (t1.Max == midmax && t1.L <= t1.r1)) {
cur.Max = t1.Max;
cur.L = t1.L;
cur.R = t1.R;
} else {
cur.Max = midmax;
cur.L = t1.r1;
cur.R = t2.l1;
}
}
return cur;
}
void build(int p, int l, int r) {
t[p].l = l; t[p].r = r;
if (l == r) {
t[p].Max = t[p].lmax = t[p].rmax = t[p].sum = a[l];
t[p].L = t[p].R = t[p].l1 = t[p].r1 = l;
return;
}
int mid = (l + r) / 2;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
t[p] = pushup(t[p * 2], t[p * 2 + 1]);
}
segt ask(int p, int l, int r) {
if (l <= t[p].l && t[p].r <= r) return t[p];
int mid = (t[p].l + t[p].r) / 2;
if (r <= mid) return ask(p * 2, l, r);
if (l > mid) return ask(p * 2 + 1, l, r);
return pushup(ask(p * 2, l, r), ask(p * 2 + 1, l, r));
}
int main() {
int tt = 1;
while (scanf("%d%d", &n, &m) != EOF) {
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
build(1, 1, n);
printf("Case %d:\n", tt++);
while (m--) {
int l, r;
scanf("%d%d", &l, &r);
segt ans = ask(1, l, r);
printf("%d %d\n", ans.L, ans.R);
}
}
return 0;
}