题目大意
很简洁,传送门。
如果你想锻炼英语,请看这里:传送门
思路
整体来看,我的代码应该是比较短、比较快的了。
这题需要维护 动态 的最大子段和。首先你可能会想到 RMQ,但是由于是动态的,而不是像 P1115 这题静态的,所以立刻放弃这种算法。
那么我们就需要功能强大的利器——线段树了。
普通的线段树大家想必都会,不会的萌新先做做 这题 吧。有兴趣,或者还想提高还可以做 这题 哦。
知道了这个,单点修改应该就没问题了,主要问题就在于 怎样用线段树维护一个区间的最大子段和呢?
为了解决这个问题,我们必须设一个结构体来存储线段树,具体代码如下:
struct node {
ll pre, suf; //前缀和、后缀和
ll maxsub, val; //最大子段和、区间和
int l, r; //子段和左端点、右端点
int pre_r, suf_l; //前缀和 右端点,后缀和 左端点
} tree[500007 * 4];
然后再贴一个单点修改的代码吧,代码只要根据结构体的变化而变化,我不做解释了:
node query(int root, int l, int r, int x, int y) {
if(l >= x && r <= y) { return tree[root]; }
int mid = (l + r) / 2;
bool flag1 = 0, flag2 = 0;
node s, b, ans;
if(x <= mid) s = query(root << 1, l, mid, x, y), flag1 = 1;
if(y > mid) b = query(root << 1 | 1, mid + 1, r, x, y), flag2 = 1;
if(flag1 && flag2) ans = renew(s, b, root);
else if(flag1) ans = s;
else if(flag2) ans = b;
return ans;
}
同时,我们还需要一个用来更新线段树的最大子段和(其实就是参数)的函数。到了这里,就需要一点点思维难度了。
首先,普通线段树模板里的 tree[root] = tree[root << 1] + tree[root << 1 | 1];
肯定是不行的。于是我们可以对于线段树每一个参数(见上)进行处理。具体实现我觉得也不太好说,仔细看我下面的代码应该就能领会到意思了:
node renew(node x, node y, int root) { //更新 tree[root]
node a = (node){0, 0, 0, x.val + y.val, 0, 0, 0, 0};
//理解这一段一定要画图,画图,画图!!!
//前缀,取最大值加入 a,同时更新前缀和的右端点
if(x.val + y.pre <= x.pre) { a.pre = x.pre; a.pre_r = x.pre_r; }
else { a.pre = x.val + y.pre; a.pre_r = y.pre_r; }
//后缀,取最大值加入 a,同时更新后缀和的左端点
if(x.suf + y.val < y.suf) { a.suf = y.suf; a.suf_l = y.suf_l; }
else { a.suf = x.suf + y.val; a.suf_l = x.suf_l; }
//字段和,取最大值加入 a,同时更新左右儿子
if(x.suf + y.pre <= x.maxsub && x.maxsub >= y.maxsub) //左子树
a.maxsub = x.maxsub, a.l = x.l, a.r = x.r;
else if(x.suf + y.pre >= x.maxsub && x.suf + y.pre >= y.maxsub) //跨越左右子树
a.maxsub = x.suf + y.pre, a.l = x.suf_l, a.r = y.pre_r;
else a.maxsub = y.maxsub, a.l = y.l, a.r = y.r; //右子树
return a;
}
ok,分析就到这了,如果有简洁的非线段树做法(作者很期待哦)欢迎私信我哦。整体代码如下:
//对拍的程序
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m, cnt = 0, w[500007];
struct node {
ll pre, suf; //前缀和、后缀和
ll maxsub, val; //最大子段和、区间和
int l, r; //子段和左端点、右端点
int pre_r, suf_l; //前缀和 右端点,后缀和 左端点
} tree[500007 * 4]; //呼 ~ 好累啊
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); }
return x * f;
}
node renew(node x, node y, int root) { //更新 tree[root]
node a = (node){0, 0, 0, x.val + y.val, 0, 0, 0, 0};
//理解这一段一定要画图,画图,画图!!!
//前缀,取最大值加入 a,同时更新前缀和的右端点
if(x.val + y.pre <= x.pre) { a.pre = x.pre; a.pre_r = x.pre_r; }
else { a.pre = x.val + y.pre; a.pre_r = y.pre_r; }
//后缀,取最大值加入 a,同时更新后缀和的左端点
if(x.suf + y.val < y.suf) { a.suf = y.suf; a.suf_l = y.suf_l; }
else { a.suf = x.suf + y.val; a.suf_l = x.suf_l; }
//字段和,取最大值加入 a,同时更新左右儿子
if(x.suf + y.pre <= x.maxsub && x.maxsub >= y.maxsub) //左子树
a.maxsub = x.maxsub, a.l = x.l, a.r = x.r;
else if(x.suf + y.pre >= x.maxsub && x.suf + y.pre >= y.maxsub) //跨越左右子树
a.maxsub = x.suf + y.pre, a.l = x.suf_l, a.r = y.pre_r;
else a.maxsub = y.maxsub, a.l = y.l, a.r = y.r; //右子树
return a;
}
inline void build_tree(int root, int l, int r) {
if(l == r) { //稍微有点改动,但是显而易见
tree[root] = (node){w[l], w[l], w[l], w[l], l, l, l, l};
return ;
}
int mid = (l + r) / 2;
build_tree(root << 1, l, mid);
build_tree(root << 1 | 1, mid + 1, r);
tree[root] = renew(tree[root << 1], tree[root << 1 | 1], root);
}
node query(int root, int l, int r, int x, int y) {
if(l >= x && r <= y) { return tree[root]; }
int mid = (l + r) / 2;
bool flag1 = 0, flag2 = 0;
node s, b, ans;
if(x <= mid) s = query(root << 1, l, mid, x, y), flag1 = 1;
if(y > mid) b = query(root << 1 | 1, mid + 1, r, x, y), flag2 = 1;
if(flag1 && flag2) ans = renew(s, b, root);
else if(flag1) ans = s;
else if(flag2) ans = b;
return ans;
}
int main() {
// freopen("live.in", "r", stdin);
// freopen("live.out", "w", stdout);
while(~scanf("%d %d", &n, &m)) { //读入技巧
memset(w, 0, sizeof(w));
memset(tree, 0, sizeof(tree)); //后面的 n * 4 + 1 可以有效缩短时间,上次网校模拟赛就是因为这个掉到 85 的 ┭┮﹏┭┮
cnt++;
printf("Case %d:\n", cnt);
for(int i = 1; i <= n; i++) w[i] = read();=
build_tree(1, 1, n);
for(int i = 1; i <= m; i++) {
int a, b;
scanf("%d %d", &a, &b);
node tmp = query(1, 1, n, a, b);
printf("%d %d\n", tmp.l, tmp.r);
}
}
return 0;
}