线段树模板

注意:以下代码均在加入如下代码的情况下运行:

#include<bits/stdc++.h>
using namespace std;

有些东西我写了,没测试过,可能有bug。大概率保证说明中的函数没有bug。

模板1:单点修改(不带懒标记)

使用说明

模板参数:typename S,int N

SegTree<S,N>是以S为元素类型,以求和为操作,以S()为默认元素,长度最大为N的线段树

要求实现S的默认构造函数,S的加法仍然能得到S且满足交换律

即对于任何x,y,z属于S,x+y属于S且(x+y)+z=x+(y+z)

void reset(int x=N,vec.tor<S> v={})

建树,长度为x,初始元素为v。若v.size()不等于x(默认不等),初始元素全都为默认元素。O(n)个加运算

const int&size() const

返回建树时的长度x,O(1)

void modify(int p,const S&v)

修改第p个元素为v。O(lgn)个加运算

S query(int a,int b) const

求[a,b)的和。O(lgn)个加运算

const S& all_query()const

返回[0,长度)的和。O(1)

const S& get(int p)const

求第p个元素。O(lgn)

int max_right<class Ch>(int l,const Ch&judge)

线段树上二分,返回一个r使得[l,r)输入judge返回true,而[l,r]输入judge返回false。O(lgn)个加运算与judge函数

范例

求范围内的和,最小值,最大值。(输入位置在[1,n]的闭区间)

const int N=5e5+100;
int n,m,a[N];
char op;int x,y;

struct Data {
    int sum,mini,maxi;

    Data(): sum(0),mini(1e9),maxi(-1e9) {}
    Data(int val): sum(val),mini(val),maxi(val) {}
    Data(int _s,int _min,int _max): sum(_s),mini(_min),maxi(_max) {}

    Data operator+(const Data& b) const {
        return Data(sum+b.sum,min(mini,b.mini),max(maxi,b.maxi));
    }

    void print() const {cout<<sum<<' '<<mini<<' '<<maxi<<endl;}
};
SegTree<Data,N> seg;

signed main() {
    cin>>n>>m;
    for(int i=0;i<n;i++) cin>>a[i];
    seg.reset(n,vector<Data>(a,a+n));

    while(m--) {
        cin>>op>>x>>y;
        if(op=='C') seg.modify(x-1,y);
        else if(op=='Q') seg.query(x-1,y).print();
    }

    return 0;
}

代码

namespace segtree {
    template<typename S,int N>
    class SegTree {
        static_assert(is_convertible<decltype(S()+S()),S>::value,"cannot get S from addition");
    private:
        struct Data {
            int l,r;
            S d;
            int lf,rf;

            int length()const{return r-l;}
            bool isLeaf()const{return length()==1;}
            int mid()const{return (l+r)>>1;}
        };

        Data d[N*2];
        int cnt;
        void calc(int x) {d[x].d=d[x].lf[d].d+d[x].rf[d].d;}

        void build(int x,int l,int r,const vector<S>& v) {
            d[x].l=l;d[x].r=r;
            if(d[x].isLeaf()) d[x].d=v[d[x].l];
            else {
                build(d[x].lf=++cnt,l,d[x].mid(),v);
                build(d[x].rf=++cnt,d[x].mid(),r,v);
                calc(x);
            }
        }

        void modify(int x,int p,const S&v) {
            if(d[x].isLeaf()) {d[x].d=v;return;}
            if(p<d[x].mid()) modify(d[x].lf,p,v);
            else modify(d[x].rf,p,v);
            calc(x);
        }

        const S&get(int x,int p) const {
            if(d[x].isLeaf()) return d[x].d;
            if(p<d[x].mid()) return get(d[x].lf,p);
            else return get(d[x].rf,p);
        }

        S query(int x,int a,int b)const{
            if(a<=d[x].l&&d[x].r<=b) return d[x].d;
            if(a<d[x].mid()) {
                S s=query(d[x].lf,a,b);
                if(b>d[x].mid()) return s+query(d[x].rf,a,b);
                return s;
            }
            return query(d[x].rf,a,b);
        }

        template<class Check>
        int _max_right(int x,S&val,int l,const Check&check) {
            if(l<=d[x].l) {
                S nv=val+d[x].d;
                if(check(nv)) {val=nv;return -1;}
                if(d[x].isLeaf()) return d[x].l;
            }
            int pos=-1;
            if(l<d[x].mid()) pos=_max_right(d[x].lf,val,l,check);
            if(pos!=-1) return pos;
            return _max_right(d[x].rf,val,l,check);
        }

    public:
        void reset(int x=N,vector<S> v={}) {
            if(v.size()!=x) v.assign(x,S());
            cnt=1;build(1,0,x,v);
        }
        const int&size() const {return d[1].r;}

        void modify(int p,const S&v) {modify(1,p,v);}
        S query(int a,int b) const {return query(1,a,b);}
        const S& all_query()const{return d[1].d;}
        const S& get(int p)const{return get(1,p);}

        template<class Check>
        int max_right(int l,const Check&check) {
            S val=this->get(l);
            if(!check(val)) return l;
            ++l;if(l==size()) return l;
            int pos=_max_right<Check>(1,val,l,check);
            return pos;
        }
        template<bool(*check)(S)>int max_right(int l)
        {return max_right(l,[](const S&s){return check(s);});}
    };
}
using segtree::SegTree;

模板2:区间修改(带懒标记)

使用说明

模板参数:typename S,class F,int N

LazySegTree<S,F,N>是以S为元素类型,以求和为操作,以S()为默认元素,以F为操作类型,以乘上元素为操作,以F()为幺元,长度最大为N的线段树

要求:

  • 实现S的默认构造函数
  • S的加法仍然能得到S
  • S的加法满足交换律
  • F对S的乘法能得到S
  • F的乘法能得到F
  • F的默认构造函数返回幺元,因此F一定要自定义结构体(用以下编写好的也可以)
  • F对S的乘法对S的加法有分配律,即\forall f\in\mathbb F\forall a,b\in\mathbb S,f*(a+b)=f*a+f*b
  • F与F与S的乘法有结合律,即\forall f,g\in\mathbb F\forall a\in\mathbb S,(f*g)*a=f*(g*a)
  • F的乘法有结合律

以下时间复杂度中常数次“下传”操作指常数次F*F与常数次F*S

void reset(int x=N,ve.ctor<S> v={})

建树,长度为x,初始元素为v。若v.size()不等于x(默认不等),初始元素全都为默认元素。O(n)个S加法

const int&size() const

返回建树时的长度x,O(1)

void modify(int p,const S&v)

修改第p个元素为v。O(lgn)个S加法,O(lgn)个下传

void apply(int p,const F&f)

把操作f应用到第p个元素。O(lgn)个S加法,O(lgn)个下传

S query(int a,int b)

求[a,b)的和。O(lgn)个S加法,O(lgn)个下传

const S& all_query()const

返回[0,长度)的和。O(1)

const S& get(int p)

求第p个元素。O(lgn)个下传

int max_right<class Ch>(int l,const Ch&judge)

线段树上二分,返回一个r使得[l,r)输入judge返回true,而[l,r]输入judge返回false。O(lgn)个S加法,O(lgn)次judge函数,O(lgn)个下传

范例

以下代码是对区间元素进行加或乘操作,输出区间和

typedef long long ll;

int p=100;

struct Data {
    int l,s;
    Data(int v=114514): l(1),s(v) {}
    Data(int len,ll sum): l(len),s(sum%p) {}
};
Data operator+(Data a,Data b){return Data(a.l+b.l,a.s+b.s);}
struct Change {
    int m,a;
    Change(ll mul=1,ll add=0): m(mul%p),a(add%p) {}
    Data operator*(Data d)const{return Data(d.l,d.s*1ll*m+d.l*1ll*a);}
};
Change operator*(Change a,Change b){return Change(a.m*1ll*b.m,b.a*1ll*a.m+a.a);}// apply a on b

const int N=100000+100;

int n,m,a[N];
LazySegTree<Data,Change,N> seg;
int op,t,g,c;

signed main() {
    cin>>n>>p;
    for(int i=1;i<=n;i++) cin>>a[i];
    seg.reset(n+10,vector<Data>(a,a+n+10));

    cin>>m;
    while(m--) {
        cin>>op>>t>>g;g++;
        if(op==1) {
            cin>>c;
            seg.apply(t,g,Change(c,0));
        }else if(op==2) {
            cin>>c;
            seg.apply(t,g,Change(1,c));
        }else if(op==3) {
            cout<<seg.query(t,g).s<<endl;
        }
    }

    return 0;
}

代码

由于代码行数巨大,可以删去不用的。

namespace segtree {
    template<typename S,class F,int N>
    class LazySegTree {
        static_assert(is_convertible<decltype(S()+S()),S>::value,"cannot combine S");
        static_assert(is_convertible<decltype(F()*S()),S>::value,"cannot apply on S");
        static_assert(is_convertible<decltype(F()*F()),F>::value,"cannot apply on F");

    public:
        struct Data {
            int l,r;
            S d;F f;
            int lf,rf;

            int length(){return r-l;}
            bool isLeaf(){return length()==1;}
            int mid(){return (l+r)>>1;}

            void operator*=(const F&ff) {d=ff*d;f=ff*f;}
        };

        Data d[N*2];
        int cnt;
        void _pushUp(int x) {d[x].d=d[x].lf[d].d+d[x].rf[d].d;}
        void _pushDown(int x) {
            if(!d[x].isLeaf()) {
                d[x].lf[d]*=d[x].f;
                d[x].rf[d]*=d[x].f;
            }
            d[x].f=F();
        }

        void _build(int x,int l,int r,const vector<S>& v) {
            d[x].l=l;d[x].r=r;d[x].f=F();
            if(d[x].isLeaf()) {
                d[x].d=v[l];
            }
            else {
                _build(d[x].lf=++cnt,l,d[x].mid(),v);
                _build(d[x].rf=++cnt,d[x].mid(),r,v);
                _pushUp(x);
            }
        }

        void _modify(int x,int p,const S&v) {
            if(d[x].isLeaf()) {d[x].d=v;return;}
            _pushDown(x);
            if(p<d[x].mid()) _modify(d[x].lf,p,v);
            else _modify(d[x].rf,p,v);
            _pushUp(x);
        }

        void _apply(int x,int p,const F&f) {
            if(d[x].isLeaf()) {d[x].d=f*d[x].d;return;}
            _pushDown(x);
            if(p<d[x].mid()) _apply(d[x].lf,p,f);
            else _apply(d[x].rf,p,f);
            _pushUp(x);
        }

        const S&_get(int x,int p) {
            if(d[x].isLeaf()) return d[x].d;
            _pushDown(x);
            return _get(p<d[x].mid()?d[x].lf:d[x].rf,p);
        }

        S _get(int x,int p)const {
            if(d[x].isLeaf()) return d[x].d;
            S ans=_get(p<d[x].mid()?d[x].lf:d[x].rf,p);
            return d[x].f*ans;
        }

        S _query(int x,int a,int b) {
            if(a<=d[x].l&&d[x].r<=b) return d[x].d;
            _pushDown(x);
            if(a<d[x].mid()) {
                S s=_query(d[x].lf,a,b);
                if(b>d[x].mid()) return s+_query(d[x].rf,a,b);
                return s;
            }
            return _query(d[x].rf,a,b);
        }

        S _query(int x,int a,int b)const{
            if(a<=d[x].l&&d[x].r<=b) return d[x].d;
            if(a<d[x].mid()) {
                S s=d[x].f*_query(d[x].lf,a,b);
                if(b>d[x].mid()) return s+d[x].f*_query(d[x].rf,a,b);
                return s;
            }
            return d[x].f*_query(d[x].rf,a,b);
        }

        void _apply(int x,int a,int b,const F&f) {
            if(a<=d[x].l&&d[x].r<=b) {d[x]*=f;return;}
            _pushDown(x);
            if(a<d[x].mid()) _apply(d[x].lf,a,b,f);
            if(b>d[x].mid()) _apply(d[x].rf,a,b,f);
            _pushUp(x);
        }

        template<class Check>
        int _max_right(int x,S&val,int l,const Check&check) {
            _pushDown(x);
            if(l<=d[x].l) {
                S nv=val+d[x].d;
                if(check(nv)) {val=nv;return -1;}
                if(d[x].isLeaf()) return d[x].l;
            }
            int pos=-1;
            if(l<d[x].mid()) pos=_max_right(d[x].lf,val,l,check);
            if(pos!=-1) return pos;
            return _max_right(d[x].rf,val,l,check);
        }

    public:
        void reset(int x=N,vector<S> v={}) {
            if(v.size()!=x) v.assign(x,S());
            cnt=1;_build(1,0,x,v);
        }
        const int&size()const{return d[1].r;}
        void modify(int p,const S&v) {_modify(1,p,v);}
        void apply(int p,const F&f) {_apply(1,p,f);}
        void apply(int l,int r,const F&f) {if(l<r)_apply(1,l,r,f);}
        S query(int a,int b) {return _query(1,a,b);}
        S query(int a,int b)const{return _query(1,a,b);}
        const S& all_query() const {return d[1].d;}
        const S& get(int p){return _get(1,p);}
        S get(int p)const{return _get(1,p);}

        template<class Check>
        int max_right(int l,const Check&check) {
            S val=this->get(l);
            if(!check(val)) return l;
            ++l;if(l==size()) return l;
            int pos=_max_right<Check>(1,val,l,check);
            return pos;
        }
        template<bool(*check)(S)>int max_right(int l)
        {return max_right(l,[](const S&s){return check(s);});}
    };
}
using segtree::LazySegTree;

其他:各种S与F

由于每次都编写同样的S与F太麻烦,我整理了一些。

data:可用于S

MinMax<typename T>

存储最大最小值。默认构造函数返回幺元(此时valid()返回false)。不用幺元正常使用的话valid()返回true。

当前联动F:MulAdd<T>

MaxSubSum<typename T>

存储最大子段和(顺便存储最大前缀和,最大后缀和,总和)。如果T的默认构造函数返回幺元,那么MaxSubSum<T>的默认构造函数也返回幺元。

change:可用于F

MulAdd<typename T>

把值先乘后加。要求1转为T是乘法幺元,0转为T是加法幺元,才能使得默认构造函数返回幺元。要求分配律,结合律。

当前联动S:T,MinMax<T>(要求乘负数不等号方向改变),等(用类似*T的方法)

代码

namespace segtree {
    namespace data {
        template<typename T>
        struct MinMax {
            T minv,maxv;
            MinMax():minv(numeric_limits<T>::max()),maxv(numeric_limits<T>::min()){}
            MinMax(const T&v):minv(v),maxv(v) {}
            MinMax(const T&_min,const T&_max):minv(_min),maxv(_max) {}
            bool valid(){return minv<=maxv;}
        };
        template<typename T>MinMax<T> operator+(const MinMax<T>&a,const MinMax<T>&b)
        {return MinMax<T>(min(a.minv,b.minv),max(a.maxv,b.maxv));}

        template<typename T>
        struct MaxSubSum {
            T lans,rans,ans,sum;
            MaxSubSum(const T&v=T()): lans(v),rans(v),ans(v),sum(v) {}
            MaxSubSum(const T&l,const T&r,const T&a,const T&s):
                lans(l),rans(r),ans(a),sum(s) {}
        };
        template<typename T>MaxSubSum<T>operator+(const MaxSubSum<T>&l,const MaxSubSum<T>&r) {
            return MaxSubSum<T>(max(l.lans,l.sum+r.lans),
                                max(r.rans,r.sum+l.rans),
                                max({l.ans,r.ans,l.rans+r.lans}),
                                l.sum+r.sum);
        }
    }
    namespace change {
        template<typename T>
        struct MulAdd {
            T m,a;
            MulAdd(): MulAdd(1,0) {}
            MulAdd(const T&mul,const T&add):m(mul),a(add){}
            T operator*(const T&v)const{return m*v+a;}

            template<typename V>auto operator*(const V&v)const{return m*v+a;}
        };
        template<typename T>MulAdd<T>operator*(const MulAdd<T>&snd,const MulAdd<T>&fst)
        {return MulAdd<T>(snd.m*fst.m,snd*fst.a);}

        template<typename T>
        struct Addition {
            T val;
            Addition():val(T()) {}
            Addition(const T&v):val(v) {}
            T operator*(const T&v)const{return val+v;}

            template<typename V>auto operator*(const V&v)const{return val+v;}
        };
        template<typename T>Addition<T>operator*
        (const Addition<T>&snd,const Addition<T>&fst){return snd*fst.val;}
    }
    template<typename T>data::MinMax<T>operator*(const change::MulAdd<T>&f,const data::MinMax<T>&v)
    {T minv=f*v.minv;T maxv=f*v.maxv;if(f.m<0)return data::MinMax<T>(maxv,minv);return data::MinMax<T>(minv,maxv);}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值