此前分析过树状数组,这两者具有解决相似问题的共同点。但线段树的功能更加强大,可以说,能用树状数组解决的问题,用线段树都可以解决,然而反之不成立。
目录
focus:
树状数组修改单点时,要修改原数组,因为原数组会被再次用到,而线段树则可以不用修改,因为原数组没有被再次使用,使用的都是seg线段数组(如果原数组会被再次使用,则也要修改)
重载了 + 运算符
体现封装的特性,好维护和拓展
- // problem :
-
- #include
- using namespace std;
- #define ll long long
- typedef pair<int, int> PII;
- #define pb push_back
- const int N = 2e5 + 5;
- int n, q, a[N];
- struct info {
- int minv, mincnt;
- };
- info operator + (const info &l, const info &r) {
- info a;
- a.minv = min(l.minv, r.minv);
- if(l.minv < r.minv)a.mincnt = l.mincnt;
- else if(l.minv > r.minv)a.mincnt = r.mincnt;
- else a.mincnt = l.mincnt + r.mincnt;
- return a;
- }
- struct node {
- info val;
- }seg[N * 4];
-
- void update(int id) {
- seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
- }
- void build(int id, int l, int r){
- if(l == r) {
- seg[id].val = {a[l], 1};
- } else {
- int mid = (l + r) / 2;
- build(id * 2, l, mid);
- build(id * 2 + 1, mid + 1, r);
- update(id);
- }
- }
-
- void change(int id, int l, int r, int pos, int val) {
- if(l == r) {
- seg[id].val = {val, 1};
- } else {
- int mid = (l + r) / 2;
- if(pos <= mid) change(id * 2, l, mid, pos, val);
- else change(id * 2 + 1, mid + 1, r, pos, val);
- update(id);
- }
- }
-
- info query(int id, int l, int r, int ql, int qr) {
- if(ql == l && qr == r) {
- return seg[id].val;
- }
- int mid = (l + r) / 2;
- if(qr <= mid) return query(id * 2, l, mid, ql, qr);
- else if(ql > mid) return query(id * 2 + 1, mid + 1, r, ql, qr);
- else return query(id * 2, l, mid, ql, mid) +
- query(id * 2 + 1, mid + 1, r, mid + 1, qr);
- }
- int main(){
- scanf("%d %d", &n, &q);
- for(int i = 1; i <= n; ++i)
- scanf("%d", &a[i]);
- build(1, 1, n);
- while(q--) {
- int ty; scanf("%d", &ty);
- if(ty == 1) {
- int pos, val; scanf("%d %d", &pos, &val);
- change(1, 1, n, pos, val);
- } else {
- int l, r; scanf("%d %d", &l, &r);
- info ans = query(1, 1, n, l, r);
- printf("%d %d\n", ans.minv, ans.mincnt);
- }
- }
-
- return 0;
- }
一般求最大字段和可以通过贪心来求解,直接for一遍数组。但显然,这题n*q明显会时间超限。
solve:
复用上一题的代码(这就是封装的好处!!!) 但有些太简单的问题,就不要封装。
只需修改线段数组中保存的信息,以及重载运算的方式即可。
focus:
a 为父节点保存的信息, l 为其左孩子保存的信息,r为右孩子保存的信息
mss: max segment sum
mpre : max prefix
msuf : max sufix
s : sum
信息具体怎么维护看代码
- // problem :
-
- #include
- using namespace std;
- #define ll long long
- typedef pair<int, int> PII;
- #define pb push_back
- const int N = 2e5 + 5;
- int n, q, a[N];
- struct info {
- ll mss, mpre, msuf, s;
- info(){}
- info(int val):mss(val), mpre(val), msuf(val), s(val){}
- };
- info operator + (const info &l, const info &r) {
- info a;
- a.mss = max({l.mss, r.mss, l.msuf + r.mpre});
- a.mpre = max(l.mpre, l.s + r.mpre);
- a.msuf = max(r.msuf, r.s + l.msuf);
- a.s = l.s + r.s;
- return a;
- }
- struct node {
- info val;
- }seg[N * 4];
-
- void update(int id) {
- seg[id].val = seg[id * 2].val + seg[id * 2 + 1].val;
- }
- void build(int id, int l, int r){
- if(l == r) {
- seg[id].val = info(a[l]);
- } else {
- int mid = (l + r) / 2;
- build(id * 2, l, mid);
- build(id * 2 + 1, mid + 1, r);
- update(id);
- }
- }
-
- void change(int id, int l, int r, int pos, int val) {
- if(l == r) {
- seg[id].val = info(val);
- } else {
- int mid = (l + r) / 2;
- if(pos <= mid) change(id * 2, l, mid, pos, val);
- else change(id * 2 + 1, mid + 1, r, pos, val);
- update(id);
- }
- }
-
- info query(int id, int l, int r, int ql, int qr) {
- if(ql == l && qr == r) {
- return seg[id].val;
- }
- int mid = (l + r) / 2;
- if(qr <= mid) return query(id * 2, l, mid, ql, qr);
- else if(ql > mid) return query(id * 2 + 1, mid + 1, r, ql, qr);
- else return query(id * 2, l, mid, ql, mid) +
- query(id * 2 + 1, mid + 1, r, mid + 1, qr);
- }
- int main(){
- scanf("%d %d", &n, &q);
- for(int i = 1; i <= n; ++i)
- scanf("%d", &a[i]);
- build(1, 1, n);
- while(q--) {
- int ty; scanf("%d", &ty);
- if(ty == 1) {
- int pos, val; scanf("%d %d", &pos, &val);
- change(1, 1, n, pos, val);
- } else {
- int l, r; scanf("%d %d", &l, &r);
- info ans = query(1, 1, n, l, r);
- printf("%lld\n", ans.mss);
- }
- }
-
- return 0;
- }
focus:
update(id) id编号的子树发生了变化,所以更新id这个节点的信息
settag(id) 给id这个节点打标记, 打标记的同时,节点的信息发生变化
pushdown(id) 将id节点的标记下传给子节点
注意tag要开ll , 因为标记可能叠加,(不着急传到子节点)
- // problem :
-
- #include
- using namespace std;
- #define ll long long
- typedef pair<int, int> PII;
- #define pb push_back
- const int N = 2E5 + 5;
- int n, q, a[N];
- struct node {
- ll t, val;
- }seg[N * 4];
-
- void update(int id) {
- seg[id].val = max(seg[id * 2].val, seg[id * 2 + 1].val);
- }
- void build(int id, int l, int r) {
- if(l == r){
- seg[id].val = a[l];
- } else {
- int mid = (l + r) / 2;
- build(id * 2, l, mid);
- build(id * 2 + 1, mid + 1, r);
- update(id);
- }
- }
- void settag(int id, ll t) {
- seg[id].val += t;
- seg[id].t += t;
- }
-
- void pushdown(int id) {
- if(seg[id].t) {
- settag(id * 2, seg[id].t);
- settag(id * 2 + 1, seg[id].t);
- seg[id].t = 0;
- }
- }
- void modify(int id, int l, int r, int ql, int qr, int t) {
- if(l == ql && r == qr) {
- settag(id, t);
- return;
- }
- pushdown(id);
- int mid = (l + r) / 2;
- if(qr <= mid) modify(id * 2, l, mid, ql, qr, t);
- else if(ql > mid) modify(id * 2 + 1, mid + 1, r, ql, qr, t);
- else modify(id * 2, l, mid, ql, mid, t),
- modify(id * 2 + 1, mid + 1, r, mid + 1, qr, t);
- update(id);
- }
-
- ll query(int id, int l, int r, int ql, int qr) {
- if(l == ql && r == qr) {
- return seg[id].val;
- }
- pushdown(id);
- int mid = (l + r) / 2;
- if(qr <= mid) return query(id * 2, l, mid, ql, qr);
- else if(ql > mid) return query(id * 2 + 1, mid + 1, r, ql, qr);
- else return max(query(id * 2, l, mid, ql, mid),
- query(id * 2 + 1, mid + 1, r, mid + 1, qr));
- }
- int main(){
- scanf("%d %d", &n, &q);
- for(int i = 1; i <= n; ++i)
- scanf("%d", &a[i]);
- build(1, 1, n);
- while(q--) {
- int ty; scanf("%d", &ty);
- if(ty == 1) {
- int l, r, t;
- scanf("%d %d %d", &l, &r, &t);
- modify(1, 1, n, l, r, t);
- } else {
- int l, r; scanf("%d %d", &l, &r);
- printf("%lld\n", query(1, 1, n, l, r));
- }
- }
-
- return 0;
- }
按常理来说,要有多个标记。但是多个标记太麻烦了,而且还要考虑先后顺序。所以就萌发出一种合并这3个标记的想法 。
focus:
标记怎么合并?
怎么根据合并后的标记对区间和进行更新
struct tag {
ll mul, add;
};
+d --> {1,d}
*d --> {d,0}
变成d ---> {0, d} 为什么这里是令所有的ai 变为d呢 ?
看代码的settag部分。起始时,tag的每个tag的初始状态为(1,0) mul变为0,则说明ai为0,要再变成d,那add部分为d就好了。 真的神奇。
- // problem :
-
- #include
- using namespace std;
- #define ll long long
- typedef pair<int, int> PII;
- #define pb push_back
- const int N = 2E5 + 5;
- int n, q, a[N];
- const int mod = 1e9 + 7;
-
- struct tag {
- ll mul, add;
- };
- tag operator + (const tag &t1, const tag &t2) {
- // (mul1 + add1) * mul2 + add2;
- return {t1.mul * t2.mul % mod, (t1.add * t2.mul % mod + t2.add) % mod};
- }
- struct node {
- tag t;
- ll val;
- int sz;
- }seg[N * 4];
-
-
- void update(int id) {
- seg[id].val = (seg[id * 2].val + seg[id * 2 + 1].val) % mod;
- }
- void build(int id, int l, int r) {
- seg[id].t = (tag){1, 0};
- seg[id].sz = r - l + 1;
- if(l == r){
- seg[id].val = a[l];
- } else {
- int mid = (l + r) / 2;
- build(id * 2, l, mid);
- build(id * 2 + 1, mid + 1, r);
- update(id);
- }
- }
- void settag(int id, tag t) {
- seg[id].val = seg[id].val * t.mul % mod + seg[id].sz * t.add % mod;
- seg[id].t = seg[id].t + t;
- }
-
- void pushdown(int id) {
- if(seg[id].t.mul != 1 || seg[id].t.add != 0) {
- settag(id * 2, seg[id].t);
- settag(id * 2 + 1, seg[id].t);
- seg[id].t.mul = 1;
- seg[id].t.add = 0;
- }
- }
- void modify(int id, int l, int r, int ql, int qr, tag t) {
- if(l == ql && r == qr) {
- settag(id, t);
- return;
- }
- pushdown(id);
- int mid = (l + r) / 2;
- if(qr <= mid) modify(id * 2, l, mid, ql, qr, t);
- else if(ql > mid) modify(id * 2 + 1, mid + 1, r, ql, qr, t);
- else modify(id * 2, l, mid, ql, mid, t),
- modify(id * 2 + 1, mid + 1, r, mid + 1, qr, t);
- update(id);
- }
-
- ll query(int id, int l, int r, int ql, int qr) {
- if(l == ql && r == qr) {
- return seg[id].val;
- }
- pushdown(id);
- int mid = (l + r) / 2;
- if(qr <= mid) return query(id * 2, l, mid, ql, qr);
- else if(ql > mid) return query(id * 2 + 1, mid + 1, r, ql, qr);
- else return (query(id * 2, l, mid, ql, mid) +
- query(id * 2 + 1, mid + 1, r, mid + 1, qr)) % mod;
- }
- int main(){
- scanf("%d %d", &n, &q);
- for(int i = 1; i <= n; ++i)
- scanf("%d", &a[i]);
- build(1, 1, n);
- while(q--) {
- int ty; scanf("%d", &ty);
- if(ty <= 3) {
- int l, r, d;
- scanf("%d %d %d", &l, &r, &d);
- if(ty == 1) modify(1, 1, n, l, r, (tag){1, d});
- else if (ty == 2) modify(1, 1, n, l, r, (tag){d, 0});
- else modify(1, 1, n, l, r, (tag){0, d});
- } else {
- int l, r; scanf("%d %d", &l, &r);
- printf("%lld\n", query(1, 1, n, l, r));
- }
- }
-
- return 0;
- }