线段树 篇四(线段树维护区间最大子段和)
目录
线段树维护区间最大子段和
前言
本身就是线段树的一种操作 单拎出来弄一个专题 例题的话就是八道 毒瘤的 \(GSS\)
还没有完成 只做了六道 后面的做完会补上
\(GSS1\)
题意
给定序列 查询给定区间中的最大子段和
思路
线段树中维护每一区间的最大前缀和 最大后缀和 区间和 并通过这些信息维护最大子段和 具体看代码
代码
/*
Time: 4.11
Worker: Blank_space
Source: SP1043 GSS1 - Can you answer these queries I
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int n, m, a[B];
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
namespace Seg {
#define ls(x) x << 1
#define rs(x) x << 1 | 1
struct node {
int l, r, mid, sum, max1, max2, len, lzy, ans;
node() {l = r = mid = max1 = sum = max2 = ans = len = lzy = 0;}
void ins(int L, int R) {l = L; r = R; mid = L + R >> 1; len = R - L + 1;}
}t[B << 2];
node operator + (const node &x, const node &y) {
node z; z.l = x.l; z.r = y.r;
z.ans = Max(Max(x.ans, y.ans), x.max2 + y.max1);
z.max1 = Max(x.max1, x.sum + y.max1); z.max2 = Max(y.max2, y.sum + x.max2);
z.sum = x.sum + y.sum;
z.mid = z.l + z.r >> 1; z.len = z.r - z.l + 1;
return z;
}
void build(int p, int l, int r) {
t[p].ins(l, r); if(l == r) {t[p].ans = t[p].max1 = t[p].max2 = t[p].sum = a[l]; return ;}
build(ls(p), l, t[p].mid); build(rs(p), t[p].mid + 1, r);
t[p] = t[ls(p)] + t[rs(p)];
}
node query(int p, int l, int r) {
if(l <= t[p].l && t[p].r <= r) return t[p];
if(l <= t[p].mid) if(r > t[p].mid) return query(ls(p), l, r) + query(rs(p), l, r);
else return query(ls(p), l, r); else return query(rs(p), l, r);
}
}
/*----------------------------------------函数*/
int main() {
n = read();
for(int i = 1; i <= n; i++) a[i] = read();
Seg::build(1, 1, n); m = read();
for(int i = 1; i <= m; i++)
{
int x = read(), y = read();
printf("%d\n", Seg::query(1, x, y).ans);
}
return 0;
}
\(GSS2\)
题意
给定序列 查询给定区间中的最大子段和 相同的数只算一次
思路
难度完爆 \(T1\)
离线维护 将元素逐一插入 同时记录元素出现的上一个位置 插入每一个数 \(x\) 的时候 将区间 \([pre_x, i]\) 加上 \(x\) 这样就避免的一个数的重复计算 对于询问离线处理 在处理完之后的区间查询询问区间的最大子段和 答案体现为这一区间的历史最大值
代码
/*
Time: 4.14
Worker: Blank_space
Source: SP1557 GSS2 - Can you answer these queries II
*/
/*--------------------------------------------*/
#include<cstdio>
#include<algorithm>
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int n, m, a[B], P[B << 1];
struct Q {int l, r, id;}q[B];
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
bool cmp(Q x, Q y) {return x.r < y.r;}
namespace Seg {
#define ls(x) x << 1
#define rs(x) x << 1 | 1
struct node {
int l, r, mid, max, hmax, lzy, hlzy;
node() {l = r = mid = max = hmax = lzy = hlzy = 0;}
void ins(int L, int R) {l = L; r = R; mid = L + R >> 1;}
}t[B << 2], ans[B];
node operator + (const node &x, const node &y) {
node z; z.l = x.l; z.r = y.r; z.mid = z.l + z.r >> 1;
z.max = Max(x.max, y.max); z.hmax = Max(x.hmax, y.hmax);
return z;
}
void build(int p, int l, int r) {
t[p].ins(l, r); if(l == r) return ;
build(ls(p), l, t[p].mid); build(rs(p), t[p].mid + 1, r);
}
void f(int p, int k, int hk) {
t[p].hmax = Max(t[p].hmax, t[p].max + hk); t[p].max += k;
t[p].hlzy = Max(t[p].hlzy, t[p].lzy + hk); t[p].lzy += k;
}
void push_down(int p) {f(ls(p), t[p].lzy, t[p].hlzy); f(rs(p), t[p].lzy, t[p].hlzy); t[p].lzy = t[p].hlzy = 0;}
void up_date(int p, int l, int r, int k) {
if(l <= t[p].l && t[p].r <= r) {f(p, k, k); return ;}
push_down(p);
if(l <= t[p].mid) up_date(ls(p), l, r, k);
if(r > t[p].mid) up_date(rs(p), l, r, k);
t[p] = t[ls(p)] + t[rs(p)];
}
node query(int p, int l, int r) {
if(l <= t[p].l && t[p].r <= r) return t[p];
push_down(p);
if(l <= t[p].mid) if(r > t[p].mid) return query(ls(p), l, r) + query(rs(p), l, r);
else return query(ls(p), l, r); else return query(rs(p), l, r);
}
}
/*----------------------------------------函数*/
int main() {
n = read();
for(int i = 1; i <= n; i++) a[i] = read();
Seg::build(1, 1, n); m = read();
for(int i = 1; i <= m; i++) q[i].l = read(), q[i].r = read(), q[i].id = i;
std::sort(q + 1, q + 1 + m, cmp);
for(int i = 1, j = 1; i <= n; i++)
{
Seg::up_date(1, P[a[i] + B] + 1, i, a[i]); P[a[i] + B] = i;
while(q[j].r == i && j <= m) Seg::ans[q[j].id] = Seg::query(1, q[j].l, q[j].r), j++;
}
for(int i = 1; i <= m; i++) printf("%d\n", Seg::ans[i].hmax);
return 0;
}
\(GSS3\)
题意
给定序列 支持修改 查询给定区间中的最大子段和
思路
\(1\) 的基础上加一个单点修改即可
代码
/*
Time: 4.11
Worker: Blank_space
Source: SP1716 GSS3 - Can you answer these queries III
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int n, m, a[B];
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
namespace Seg {
#define ls(x) x << 1
#define rs(x) x << 1 | 1
struct node {
int l, r, mid, len, max1, max2, sum, ans;
node() {l = r = mid = len = max1 = max2 = sum = ans = 0;}
void ins(int L, int R) {l = L; r = R; mid = L + R >> 1; len = R - L + 1;}
}t[B << 2];
node operator + (const node &x, const node &y) {
node z; z.l = x.l; z.r = y.r; z.mid = z.l + z.r >> 1; z.len = z.r - z.l + 1;
z.ans = Max(Max(x.ans, y.ans), x.max2 + y.max1);
z.max1 = Max(x.max1, x.sum + y.max1); z.max2 = Max(y.max2, y.sum + x.max2);
z.sum = x.sum + y.sum;
return z;
}
void build(int p, int l, int r) {
t[p].ins(l, r); if(l == r) {t[p].max1 = t[p].max2 = t[p].sum = t[p].ans = a[l]; return ;}
build(ls(p), l, t[p].mid); build(rs(p), t[p].mid + 1, r);
t[p] = t[ls(p)] + t[rs(p)];
}
void up_date(int p, int pos, int k) {
if(t[p].l == t[p].r && t[p].l == pos) {t[p].max1 = t[p].max2 = t[p].sum = t[p].ans = k; return ;}
if(pos <= t[p].mid) up_date(ls(p), pos, k); else up_date(rs(p), pos, k);
t[p] = t[ls(p)] + t[rs(p)];
}
node query(int p, int l, int r) {
if(l <= t[p].l && t[p].r <= r) return t[p];
if(l <= t[p].mid) if(r > t[p].mid) return query(ls(p), l, r) + query(rs(p), l, r);
else return query(ls(p), l, r); else return query(rs(p), l, r);
}
}
/*----------------------------------------函数*/
int main() {
n = read();
for(int i = 1; i <= n; i++) a[i] = read();
Seg::build(1, 1, n); m = read();
for(int i = 1; i <= m; i++)
{
int opt = read(), x = read(), y = read();
if(opt == 0) Seg::up_date(1, x, y);
if(opt == 1) printf("%d\n", Seg::query(1, x, y).ans);
}
return 0;
}
\(GSS4\)
题意
给定序列 区间开方 区间求和
思路
势能线段树板子
代码
/*
Time: 4.11
Worker: Blank_space
Source: SP2713 GSS4 - Can you answer these queries IV
势能线段树
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cmath>
#define int long long
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int T, n, m, a[B], b[B], c[B], cnt;
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
namespace Seg {
#define ls(x) x << 1
#define rs(x) x << 1 | 1
struct node {
int l, r, mid, sum, max;
node() {l = r = mid = sum = max = 0;}
void ins(int L, int R) {l = L; r = R; mid = L + R >> 1;}
}t[B << 2];
node operator + (const node &x, const node &y) {
node z; z.l = x.l; z.r = y.r; z.mid = z.l + z.r >> 1;
z.sum = x.sum + y.sum; z.max = Max(x.max, y.max);
return z;
}
void build(int p, int l, int r) {
t[p].ins(l, r); if(l == r) {t[p].sum = t[p].max = a[l]; return ;}
build(ls(p), l, t[p].mid); build(rs(p), t[p].mid + 1, r);
t[p] = t[ls(p)] + t[rs(p)];
}
void up_date(int p, int l, int r) {
if(l <= t[p].l && t[p].r <= r && t[p].l == t[p].r) {t[p].max = t[p].sum = std::sqrt(t[p].sum); return ;}
if(l <= t[p].mid && t[ls(p)].max > 1) up_date(ls(p), l, r);
if(r > t[p].mid && t[rs(p)].max > 1) up_date(rs(p), l, r);
t[p] = t[ls(p)] + t[rs(p)];
}
node query(int p, int l, int r) {
if(l <= t[p].l && t[p].r <= r) return t[p];
if(l <= t[p].mid) if(r > t[p].mid) return query(ls(p), l, r) + query(rs(p), l, r);
else return query(ls(p), l, r); else return query(rs(p), l, r);
}
}
/*----------------------------------------函数*/
signed main() {
while(~scanf("%lld", &n))
{
printf("Case #%lld:\n", ++T);
for(int i = 1; i <= n; i++) a[i] = read();
Seg::build(1, 1, n); m = read();
for(int i = 1; i <= m; i++)
{
int opt = read(), x = read(), y = read(); if(x > y) Swap(x, y);
if(opt == 0) Seg::up_date(1, x, y);
if(opt == 1) printf("%lld\n", Seg::query(1, x, y).sum);
}
}
return 0;
}
\(GSS5\)
题意
给定序列 查询左端点在区间 \([l_1, r_1]\) 中 右端点在 \([l_2, r_2]\) 中的区间最大子段和
思路
上面的板子搬过来
只考虑查询
分类讨论
- 当区间没有重合的时候 中间的一段必选 左边取最大后缀 右边取最大前缀
- 当区间出现重合时 分别合并 \([l_1, l_2]\) 与 \([l_2, r2]\) 和 \([l_1, r_1]\) 和 \([r_1, r_2]\) 减掉重复计算的部分 特判区间 \([l_2, r_1]\) 三者取最大值
上面去重的时候计算完后再单独减 不要在区间上减 会出问题
代码
/*
Time: 4.14
Worker: Blank_space
Source: SP2916 GSS5 - Can you answer these queries V
*/
/*--------------------------------------------*/
#include<cstdio>
#include<cstring>
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int T, n, m, a[A];
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
namespace Seg {
#define ls(x) x << 1
#define rs(x) x << 1 | 1
struct node {
int l, r, mid, sum, max, max1, max2;
node() {l = r = mid = sum = max = max1 = max2 = 0;}
void ins(int L, int R) {l = L; r = R; mid = L + R >> 1;}
}t[A << 2];
node operator + (const node &x, const node &y) {
node z; z.l = x.l; z.r = y.r; z.mid = z.l + z.r >> 1;
z.max1 = Max(x.max1, x.sum + y.max1); z.max2 = Max(y.max2, y.sum + x.max2);
z.max = Max(Max(x.max, y.max), x.max2 + y.max1); z.sum = x.sum + y.sum;
return z;
}
void build(int p, int l, int r) {
t[p].ins(l, r); if(l == r) {t[p].max = t[p].sum = t[p].max1 = t[p].max2 = a[l]; return ;}
build(ls(p), l, t[p].mid); build(rs(p), t[p].mid + 1, r);
t[p] = t[ls(p)] + t[rs(p)];
}
node query(int p, int l, int r) {
if(l > r) return t[0]; if(l <= t[p].l && t[p].r <= r) return t[p];
if(l <= t[p].mid) if(r > t[p].mid) return query(ls(p), l, r) + query(rs(p), l, r);
else return query(ls(p), l, r); else return query(rs(p), l, r);
}
int Query(int l1, int r1, int l2, int r2) {
if(r1 < l2) return query(1, l1, r1).max2 + query(1, r1 + 1, l2 - 1).sum + query(1, l2, r2).max1;
int res = query(1, l2, r1).max;
if(l1 < l2) res = Max(res, query(1, l1, l2).max2 + query(1, l2, r2).max1 - a[l2]);
if(r1 < r2) res = Max(res, query(1, l1, r1).max2 + query(1, r1, r2).max1 - a[r1]);
return res;
}
}
void work() {
n = read();
for(int i = 1; i <= n; i++) a[i] = read();
Seg::build(1, 1, n); m = read();
for(int i = 1; i <= m; i++)
{
int l1 = read(), r1 = read(), l2 = read(), r2 = read();
printf("%d\n", Seg::Query(l1, r1, l2, r2));
}
}
/*----------------------------------------函数*/
int main() {
T = read(); while(T--) work();
return 0;
}
\(GSS6\)
没做
\(GSS7\)
题意
给定 \(n\) 个节点的树 支持路径修改与路径最大子段和查询
思路
树剖套线段树 直接摁上
链的修改与合并照常 唯一特殊的是跳到一条链上的时候 要注意链与区间的对应关系
由于合并的时候是由两端向中间合并的 有一个区间的左右是反着的 我说不太清楚 可以自己悟一下
代码
/*
Time: 4.14
Worker: Blank_space
Source: SP6779 GSS7 - Can you answer these queries VII
*/
/*--------------------------------------------*/
#include<cstdio>
#include<algorithm>
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*--------------------------------------头文件*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*------------------------------------常量定义*/
int n, m, a[B], pos[B], cnt;
struct edge {int v, nxt;}e[B << 1];
int head[B], ecnt;
/*------------------------------------变量定义*/
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 << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
/*----------------------------------------快读*/
void add_edge(int u, int v) {e[++ecnt] = (edge){v, head[u]}; head[u] = ecnt;}
namespace Seg {
#define ls(x) x << 1
#define rs(x) x << 1 | 1
struct node {
int l, r, mid, max, max1, max2, sum, len, lzy;
node() {l = r = mid = max = max1 = max2 = sum = len = 0; lzy = INF;}
void ins(int L, int R) {l = L; r = R; mid = L + R >> 1; len = R - L + 1;}
}t[B << 2];
node operator + (const node &x, const node &y) {
node z; z.l = x.l; z.r = y.r; z.mid = z.l + z.r >> 1; z.len = z.r - z.l + 1;
z.max = Max(Max(x.max, y.max), x.max2 + y.max1);
z.max1 = Max(x.max1, x.sum + y.max1); z.max2 = Max(y.max2, y.sum + x.max2);
z.sum = x.sum + y.sum;
return z;
}
void build(int p, int l, int r) {
t[p].ins(l, r); if(l == r) {t[p].sum = a[pos[l]]; t[p].max = t[p].max1 = t[p].max2 = Max(t[p].sum, 0); return ;}
build(ls(p), l, t[p].mid); build(rs(p), t[p].mid + 1, r);
t[p] = t[ls(p)] + t[rs(p)];
}
void f(int p, int k) {t[p].sum = t[p].len * k; t[p].max = t[p].max1 = t[p].max2 = Max(t[p].sum, 0); t[p].lzy = k;}
void push_down(int p) {f(ls(p), t[p].lzy); f(rs(p), t[p].lzy); t[p].lzy = INF;}
void up_date(int p, int l, int r, int k) {
if(l <= t[p].l && t[p].r <= r) {f(p, k); return ;}
if(t[p].lzy != INF) push_down(p);
if(l <= t[p].mid) up_date(ls(p), l, r, k);
if(r > t[p].mid) up_date(rs(p), l, r, k);
t[p] = t[ls(p)] + t[rs(p)];
}
node query(int p, int l, int r) {
if(l <= t[p].l && t[p].r <= r) return t[p];
if(t[p].lzy != INF) push_down(p);
if(l <= t[p].mid) if(r > t[p].mid) return query(ls(p), l, r) + query(rs(p), l, r);
else return query(ls(p), l, r); else return query(rs(p), l, r);
}
}
namespace Cut {
int siz[B], son[B], dep[B], top[B], dfn[B], fa[B];
void dfs(int u, int pre) {
siz[u] = 1; dep[u] = dep[pre] + 1; fa[u] = pre;
for(int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].v; if(v == pre) continue;
dfs(v, u); siz[u] += siz[v];
if(!son[u] || siz[son[u]] < siz[v]) son[u] = v;
}
}
void dfs2(int u, int tp) {
dfn[u] = ++cnt; pos[cnt] = u; top[u] = tp;
if(!son[u]) return ; dfs2(son[u], tp);
for(int i = head[u]; i; i = e[i].nxt)
{
int v = e[i].v;
if(v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
void up_date(int x, int y, int k) {
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) Swap(x, y);
Seg::up_date(1, dfn[top[x]], dfn[x], k);
x = fa[top[x]];
}
if(dfn[x] > dfn[y]) Swap(x, y);
Seg::up_date(1, dfn[x], dfn[y], k);
}
int query(int x, int y) {
Seg::node _x, _y;
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) Swap(x, y), std::swap(_x, _y);
_x = Seg::query(1, dfn[top[x]], dfn[x]) + _x;
x = fa[top[x]];
}
if(dfn[x] > dfn[y]) std::swap(_x, _y), Swap(x, y);
Swap(_x.max1, _x.max2); _x = _x + Seg::query(1, dfn[x], dfn[y]); _y = _x + _y;
return _y.max;
}
}
/*----------------------------------------函数*/
int main() {
n = read();
for(int i = 1; i <= n; i++) a[i] = read();
for(int i = 1; i < n; i++)
{
int x = read(), y = read();
add_edge(x, y); add_edge(y, x);
}
Cut::dfs(1, 0); Cut::dfs2(1, 1); Seg::build(1, 1, cnt); m = read();
for(int i = 1; i <= m; i++)
{
int opt = read(), x = read(), y = read();
if(opt == 1) printf("%d\n", Cut::query(x, y));
if(opt == 2) {int z = read(); Cut::up_date(x, y, z);}
}
return 0;
}