好久之前(好像是去年5月)做过的题,居然没做出来就弃疗了,今天才发现
问题的本质其实是询问区间最大和
用线段树怎么上传?
子树上传到根的最大字段分成两种情况:
- 不经过终点,是两段的最大值
- 经过中点
经过中点怎么完成?
定义:
d
a
t
dat
dat 表示区间最大字段和
s
u
m
sum
sum 表示区间和
l
m
a
x
lmax
lmax 表示区间左端点的最大子段和
r
m
a
x
rmax
rmax 表示区间右端点的最大子段和
怎么上传?
s
u
m
sum
sum 直接上传,
l
m
a
x
[
x
]
=
m
a
x
(
l
m
a
x
[
l
]
,
s
u
m
[
l
]
+
l
m
a
x
[
r
]
)
lmax[x]=max(lmax[l],sum[l]+lmax[r])
lmax[x]=max(lmax[l],sum[l]+lmax[r])
r
m
a
x
[
x
]
=
m
a
x
(
r
m
a
x
[
r
]
,
s
u
m
[
r
]
+
r
m
a
x
[
l
]
)
rmax[x]=max(rmax[r],sum[r]+rmax[l])
rmax[x]=max(rmax[r],sum[r]+rmax[l])
d
a
t
[
x
]
=
m
a
x
(
d
a
t
[
l
]
+
d
a
t
[
r
]
,
l
m
a
x
[
l
]
+
l
m
a
x
[
r
]
)
dat[x]=max(dat[l]+dat[r],lmax[l]+lmax[r])
dat[x]=max(dat[l]+dat[r],lmax[l]+lmax[r])
#include <map>
#include <set>
#include <ctime>
#include <queue>
#include <stack>
#include <cmath>
#include <vector>
#include <bitset>
#include <cstdio>
#include <cctype>
#include <string>
#include <numeric>
#include <cstring>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std ;
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define loop(s, v, it) for (s::iterator it = v.begin(); it != v.end(); it++)
#define cont(i, x) for (int i = head[x]; i; i = e[i].nxt)
#define clr(a) memset(a, 0, sizeof(a))
#define ass(a, sum) memset(a, sum, sizeof(a))
#define lowbit(x) (x & -x)
#define all(x) x.begin(), x.end()
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define iv inline void
#define enter cout << endl
#define siz(x) ((int)x.size())
#define file(s) freopen(s".in", "r", stdin), freopen(s."out", "w", stdout)
typedef long long ll ;
typedef unsigned long long ull ;
typedef pair <int, int> pii ;
typedef vector <int> vi ;
typedef vector <pii> vii ;
typedef queue <int> qi ;
typedef set <int> si ;
typedef map <int, int> mii ;
typedef map <string, int> msi ;
const int N = 50010 ;
const int INF = 0x3f3f3f3f ;
const int iinf = 1 << 30 ;
const ll linf = 2e18 ;
const int MOD = 1000000007 ;
const double eps = 1e-7 ;
void print(int x) { cout << x << endl ; exit(0) ; }
void PRINT(string x) { cout << x << endl ; exit(0) ; }
void douout(double x){ printf("%lf\n", x + 0.0000000001) ; }
int n, m ;
int a[N] ;
struct Tree {
int lmax, rmax, sum, dat ;
} tr[N << 2] ;
#define ls x << 1
#define rs x << 1 | 1
void pushup(int x) {
Tree l = tr[ls], r = tr[rs] ;
tr[x].sum = l.sum + r.sum ;
tr[x].lmax = max(l.lmax, l.sum + r.lmax) ;
tr[x].rmax = max(r.rmax, r.sum + l.rmax) ;
tr[x].dat = max(l.rmax + r.lmax, max(l.dat, r.dat)) ;
}
void build(int x, int l, int r) {
if (l == r) {
tr[x].dat = tr[x].lmax = tr[x].rmax = tr[x].sum = a[l] ;
return ;
}
int mid = (l + r) >> 1 ;
build(ls, l, mid) ;
build(rs, mid + 1, r) ;
pushup(x) ;
}
void modify(int x, int l, int r, int pos, int val) {
if (l == r) {
tr[x].dat = tr[x].lmax = tr[x].rmax = tr[x].sum = val ;
return ;
}
int mid = (l + r) >> 1 ;
if (pos <= mid) modify(ls, l, mid, pos, val) ;
else modify(rs, mid + 1, r, pos, val) ;
pushup(x) ;
}
Tree query(int x, int l, int r, int X, int Y) {
if (X <= l && r <= Y) return tr[x] ;
int mid = (l + r) >> 1 ;
if (Y <= mid) return query(ls, l, mid, X, Y) ;
if (X > mid) return query(rs, mid + 1, r, X, Y) ;
Tree L = query(ls, l, mid, X, mid), R = query(rs, mid + 1, r, mid + 1, Y), res ;
res.sum = L.sum + R.sum ;
res.lmax = max(L.lmax, L.sum + R.lmax) ;
res.rmax = max(R.rmax, R.sum + L.rmax) ;
res.dat = max(L.rmax + R.lmax, max(L.dat, R.dat)) ;
return res ;
}
signed main(){
scanf("%d", &n) ;
rep(i, 1, n) scanf("%d", &a[i]) ;
build(1, 1, n) ;
scanf("%d", &m) ;
while (m--) {
int op, x, y ; scanf("%d%d%d", &op, &x, &y) ;
if (op == 0) modify(1, 1, n, x, y) ;
else printf("%d\n", query(1, 1, n, x, y).dat) ;
}
return 0 ;
}
/*
写代码时请注意:
1.ll?数组大小,边界?数据范围?
2.精度?
3.特判?
4.至少做一些
思考提醒:
1.最大值最小->二分?
2.可以贪心么?不行dp可以么
3.可以优化么
4.维护区间用什么数据结构?
5.统计方案是用dp?模了么?
6.逆向思维?
*/