2013-team4/code/seg-tree

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

{{{
uva 11992
}}}
{{{
#include <cstdio>
#include <iostream>
#include <utility>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;
typedef pair<int, PII> PIII;

template<class T> void takemin(T &a, const T &b) {a = min(a, b);}
template<class T> void takemax(T &a, const T &b) {a = max(a, b);}

const int INF = 2100000000;

void gao(PIII &a, const PIII &b){
    a.first += b.first;
    takemin(a.second.first, b.second.first);
    takemax(a.second.second, b.second.second);
}

struct Segtree{
    struct Node{
        int l, r, m, lch, rch;
        int mi, mx, sum;
        bool setf, addf;
        int setv, addv;
    }node[65536 * 32];

    void set(Node &o, int v){
        if(o.addf){
            o.addf = false;
        }
        o.setf = true;
        o.setv = v;
        o.mi = o.mx = v;
        o.sum = (o.r - o.l + 1) * v;
    }

    void add(Node &o, int v){
        if(o.setf){
            set(o, o.setv + v);
        }
        else{
            if(o.addf) o.addv += v;
            else o.addv = v;
            o.addf = true;
            o.mi += v; o.mx += v;
            o.sum += (o.r - o.l + 1) * v;
        }
    }

    void push_down(Node &o){
        if(o.setf){
            o.setf = false;
            set(node[o.lch], o.setv);
            set(node[o.rch], o.setv);
        }
        else if(o.addf){
            o.addf = false;
            add(node[o.lch], o.addv);
            add(node[o.rch], o.addv);
        }
    }

    void pull_up(Node &o){
        Node &L = node[o.lch], &R = node[o.rch];
        o.mi = min(L.mi, R.mi);
        o.mx = max(L.mx, R.mx);
        o.sum = L.sum + R.sum;
    }

    void build(int rt, int l, int r){
        Node &o = node[rt];
        o.l = l; o.r = r; o.m = (l + r) / 2;
        o.setf = o.addf = false;
        o.mi = o.mx = o.sum = 0;
        if(l < r){
            o.lch = rt * 2;
            o.rch = o.lch + 1;
            build(o.lch, o.l, o.m);
            build(o.rch, o.m + 1, o.r);
        }
    }

    void update(int rt, int l, int r, int v, bool type){
        Node &o = node[rt];
        if(l <= o.l && o.r <= r){
            if(type == true) add(o, v);
            else set(o, v);
            return;
        }
        push_down(o);
        if(l <= o.m) update(o.lch, l, r, v, type);
        if(r >= o.m + 1) update(o.rch, l, r, v, type);
        pull_up(o);
    }

    PIII query(int rt, int l, int r){
        Node &o = node[rt];
        if(l <= o.l && o.r <= r){
            return PIII(o.sum, PII(o.mi, o.mx));
        }
        push_down(o);
        PIII ret = PIII(0, PII(INF, -INF));
        if(l <= o.m) gao(ret, query(o.lch, l, r));
        if(r >= o.m + 1) gao(ret, query(o.rch, l, r));
        return ret;
    }
}tree[21];

int main(){
    int r, c, m;
    while(3 == scanf("%d %d %d", &r, &c, &m)){
        for(int i = 1; i <= r; i++)
            tree[i].build(1, 1, c);
        while(m--){
            int op, x1, x2, y1, y2, v;
            scanf("%d %d %d %d %d", &op, &x1, &y1, &x2, &y2);
            if(op != 3){
                scanf("%d", &v);
                for(int i = x1; i <= x2; i++)
                    tree[i].update(1, y1, y2, v, bool(op == 1));
            }
            else{
                PIII ans = PIII(0, PII(INF, -INF));
                for(int i = x1; i <= x2; i++){
                    gao(ans, tree[i].query(1, y1, y2));
                }
                printf("%d %d %d\n", ans.first, ans.second.first, ans.second.second);
            }
        }
    }
    return 0;
}
}}}

{{{
多个懒标记的线段树
做法:将懒标记规定优先级,每次pushdown的时候根据优先级来pushdown
比如:同时存在3种区间操作:1.整段区间赋值为一个数 2.整段区间加上一个数 3.整段区间乘上一个数
这时候我们规定cover操作的优先级最高,加法和乘法可以利用分配律/结合律同时处理
例子:BZOJ 1798,UVA 11402
}}}

{{{
线段树区间开方
处理方法:每个数一般小于2^32次,也就是说一个数最多开方7次就会变成1(0除外)
于是我们可以暴力开方,每次维护一个区间最大值,当这个区间最大值小于等于1的时候,就不用给这个区间开方了
时间复杂度是log级的
例子:BZOJ 3211,SPOJ GSS4
}}}

{{{
求区间的平方和,或者立方和,或者p次方和(一般p<=3,最大不会超过4),修改操作包括单点修改和成段修改(有覆盖,加,乘)
处理方法:二项式展开,以平方和为例,成段修改操作只有加法
(A[i]+x)^2=A[i]^2+2*A[i]*x+x^2
观察上面式子,我们知道这棵线段树需要维护ΣA[i]^2和ΣA[i],这样就好了
例子:SPOJ SEGSQRSS
}}}
uva 11992
#include <cstdio>
#include <iostream>
#include <utility>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
typedef pair<int, PII> PIII;
template<class T> void takemin(T &a, const T &b) {a = min(a, b);}
template<class T> void takemax(T &a, const T &b) {a = max(a, b);}
const int INF = 2100000000;
void gao(PIII &a, const PIII &b){
    a.first += b.first;
    takemin(a.second.first, b.second.first);
    takemax(a.second.second, b.second.second);
}
struct Segtree{
    struct Node{
        int l, r, m, lch, rch;
        int mi, mx, sum;
        bool setf, addf;
        int setv, addv;
    }node[65536 * 32];
    void set(Node &o, int v){
        if(o.addf){
            o.addf = false;
        }
        o.setf = true;
        o.setv = v;
        o.mi = o.mx = v;
        o.sum = (o.r - o.l + 1) * v;
    }
    void add(Node &o, int v){
        if(o.setf){
            set(o, o.setv + v);
        }
        else{
            if(o.addf) o.addv += v;
            else o.addv = v;
            o.addf = true;
            o.mi += v; o.mx += v;
            o.sum += (o.r - o.l + 1) * v;
        }
    }
    void push_down(Node &o){
        if(o.setf){
            o.setf = false;
            set(node[o.lch], o.setv);
            set(node[o.rch], o.setv);
        }
        else if(o.addf){
            o.addf = false;
            add(node[o.lch], o.addv);
            add(node[o.rch], o.addv);
        }
    }
    void pull_up(Node &o){
        Node &L = node[o.lch], &R = node[o.rch];
        o.mi = min(L.mi, R.mi);
        o.mx = max(L.mx, R.mx);
        o.sum = L.sum + R.sum;
    }
    void build(int rt, int l, int r){
        Node &o = node[rt];
        o.l = l; o.r = r; o.m = (l + r) / 2;
        o.setf = o.addf = false;
        o.mi = o.mx = o.sum = 0;
        if(l < r){
            o.lch = rt * 2;
            o.rch = o.lch + 1;
            build(o.lch, o.l, o.m);
            build(o.rch, o.m + 1, o.r);
        }
    }
    void update(int rt, int l, int r, int v, bool type){
        Node &o = node[rt];
        if(l <= o.l && o.r <= r){
            if(type == true) add(o, v);
            else set(o, v);
            return;
        }
        push_down(o);
        if(l <= o.m) update(o.lch, l, r, v, type);
        if(r >= o.m + 1) update(o.rch, l, r, v, type);
        pull_up(o);
    }
    PIII query(int rt, int l, int r){
        Node &o = node[rt];
        if(l <= o.l && o.r <= r){
            return PIII(o.sum, PII(o.mi, o.mx));
        }
        push_down(o);
        PIII ret = PIII(0, PII(INF, -INF));
        if(l <= o.m) gao(ret, query(o.lch, l, r));
        if(r >= o.m + 1) gao(ret, query(o.rch, l, r));
        return ret;
    }
}tree[21];
int main(){
    int r, c, m;
    while(3 == scanf("%d %d %d", &r, &c, &m)){
        for(int i = 1; i <= r; i++)
            tree[i].build(1, 1, c);
        while(m--){
            int op, x1, x2, y1, y2, v;
            scanf("%d %d %d %d %d", &op, &x1, &y1, &x2, &y2);
            if(op != 3){
                scanf("%d", &v);
                for(int i = x1; i <= x2; i++)
                    tree[i].update(1, y1, y2, v, bool(op == 1));
            }
            else{
                PIII ans = PIII(0, PII(INF, -INF));
                for(int i = x1; i <= x2; i++){
                    gao(ans, tree[i].query(1, y1, y2));
                }
                printf("%d %d %d\n", ans.first, ans.second.first, ans.second.second);
            }
        }
    }
    return 0;
}
多个懒标记的线段树
做法:将懒标记规定优先级,每次pushdown的时候根据优先级来pushdown
比如:同时存在3种区间操作:1.整段区间赋值为一个数 2.整段区间加上一个数 3.整段区间乘上一个数
这时候我们规定cover操作的优先级最高,加法和乘法可以利用分配律/结合律同时处理
例子:BZOJ 1798,UVA 11402
线段树区间开方
处理方法:每个数一般小于2^32次,也就是说一个数最多开方7次就会变成1(0除外)
于是我们可以暴力开方,每次维护一个区间最大值,当这个区间最大值小于等于1的时候,就不用给这个区间开方了
时间复杂度是log级的
例子:BZOJ 3211,SPOJ GSS4
求区间的平方和,或者立方和,或者p次方和(一般p<=3,最大不会超过4),修改操作包括单点修改和成段修改(有覆盖,加,乘)
处理方法:二项式展开,以平方和为例,成段修改操作只有加法
(A[i]+x)^2=A[i]^2+2*A[i]*x+x^2
观察上面式子,我们知道这棵线段树需要维护ΣA[i]^2和ΣA[i],这样就好了
例子:SPOJ SEGSQRSS