20190729 线段树

线段树真是一个很重要很重要的数据结构!!!

一、概念

线段树是一棵二叉树,每个节点维护序列的一段区间

二、复杂度

o(nlogn)

开数组一般要开四倍空间

三、相关量

儿子:除了叶子节点,每个节点都有左儿子和右儿子

左儿子:左儿子的节点编号是父节点的两倍2 * ro,对应父节点左一半区间【lmid

右儿子:右儿子的节点编号是父节点的二倍加一2 * ro + 1,对应父节点右一半区间【mid + 1r

四、一些操作

1、建树o(n)

我比较喜欢用结构体存每个节点的信息

struct node
{
	LL l,r,w,laz;
}tr[400005]

 l存区间左端点,r存区间右端点,w存节点权值(区间和),laz是加法乘法标记,数组下标是节点编号

然后就是递归建图

void build(LL ro,LL ll,LL rr)
{
    tree[ro].l = ll;
    tree[ro].r = rr;
    if(ll == rr)
    {
        scanf("%lld",&tree[ro].w);
        tree[ro].c = 1;
        tree[ro].f = 0;
        return;
    }
    LL mid = (ll + rr) / 2;
    build(ro * 2,ll,mid);
    build(ro * 2 + 1,mid + 1,rr);
    tree[ro].w = tree[ro * 2].w + tree[ro * 2 + 1].w;
    tree[ro].w %= mod;
    tree[ro].c = 1;
    tree[ro].f = 0;
}

2、单点修改&区间修改o(logn)

记得每次修改前都先下穿一下lazy标记,先乘再加

void down(LL ro)
{   
    tree[ro * 2].c *= tree[ro].c;
    tree[ro * 2 + 1].c *= tree[ro].c;
    tree[ro * 2].c %= mod;
    tree[ro * 2 + 1].c %= mod;
    
    tree[ro * 2].f = tree[ro * 2].f * tree[ro].c + tree[ro].f;
    tree[ro * 2 + 1].f = tree[ro * 2 + 1].f * tree[ro].c + tree[ro].f;
    tree[ro * 2].f %= mod;
    tree[ro * 2 + 1].f %= mod;
    
    tree[ro * 2].w = tree[ro * 2].w * tree[ro].c +  tree[ro].f * (tree[ro * 2].r - tree[ro * 2].l + 1);
    tree[ro * 2 + 1].w = tree[ro * 2 + 1].w * tree[ro].c + tree[ro].f * (tree[ro * 2 + 1].r - tree[ro * 2 + 1].l + 1);
    tree[ro].w = tree[ro * 2].w + tree[ro * 2 + 1].w;
    tree[ro * 2].w %= mod;
    tree[ro * 2 + 1].w %= mod;
    tree[ro].w %= mod;
    
    tree[ro].f = 0;
    tree[ro].c = 1;
}
void changeadd(LL ro)
{
    if(tree[ro].l >= lll && tree[ro].r <= rrr)
    {
        tree[ro].w += (tree[ro].r - tree[ro].l + 1) * xxx;
        tree[ro].f += xxx;
        tree[ro].w %= mod;
        tree[ro].f %= mod;
        return ;
    }
    down(ro);
    LL mid = (tree[ro].l + tree[ro].r) / 2;
    if(lll <= mid)changeadd(ro * 2);
    if(rrr > mid)changeadd(ro * 2 + 1);
    tree[ro].w = tree[ro * 2].w + tree[ro * 2 + 1].w;
    return ;
}
void changemul(LL ro)
{
    if(tree[ro].l >= lll && tree[ro].r <= rrr)
    {
        tree[ro].w *= xxx;
        tree[ro].c *= xxx;
        tree[ro].f *= xxx;
        tree[ro].w %= mod;
        tree[ro].c %= mod;
        tree[ro].f %= mod;
        return ;
    }
    down(ro);
    LL mid = (tree[ro].l + tree[ro].r) / 2;
    if(lll <= mid)changemul(ro * 2);
    if(rrr > mid)changemul(ro * 2 + 1);
    tree[ro].w = tree[ro * 2].w + tree[ro * 2 + 1].w;
    tree[ro].w %= mod;
    return ;
}

3、单点查询&区间查询o(logn)

每次查询前也不要忘了下传lazy标记

void ask(LL ro)
{
    if(tree[ro].l >= lll && tree[ro].r <= rrr)
    {
        ans += tree[ro].w;
        ans %= mod;
        return ;
    }
    down(ro);
    int mid = (tree[ro].l + tree[ro].r) / 2;
    if(lll <= mid)ask(ro * 2);
    if(rrr > mid)ask(ro * 2 + 1);
}

五、题

洛谷p3372【模板】线段树1

洛谷p3373【模板】线段树2

没事应该常回来看看刷刷

原文地址:https://www.cnblogs.com/djfuuxjz/p/11264410.html