点更新线段树模板

点更新线段树

对一列数,对每次询问输出对应区间的和,每次修改只修改一个数的值。。。

定义:

struct tree
{
    int l,r;//记录代表的区间
    int sum;//代表区间的和
}


由于线段树还是相对比较平衡的,所以可以使用数组t来存储这棵树,
对与某个节点i,t[i*2]就是左子树,t[i*2+1]就是右子树
建树:线段树的思想是每个节点记录区间的信息,某点区间1~n,则1~n/2为其左子树,n/2+1~n为其右子树,每次建立子树方法相同,可以递归建树。。。
至于询问,如果询问区间并不是树上某个确切的节点,就拆分开落到其子树上,追究到底还是递归。。
加和,现在是对点加和还是比较容易的,回溯时将每个节点的sum值更新即可。。。

tree数组开的大小一般是数据数组a大小的4倍

模板的注意事项

一开始数据读入a数组中,需要的话请自行更改成其他名字。

buildtree(id,l,r)   初次调用id一般是头结点0,l与r是构造的范围,初次调用为1~n或者0~n-1,注意buildtree时要求a中有值,如果建立一个空树,需要把a清零,或者把函数中a[i]出现的地方改为0

update(id,s,w)   初次调用id一般是头结点0,s为需要修改的位置的下标,w为要修改成的值

sum(id,l,r)         初次调用id一般是头结点0,求下标l到r的和的值。

线段树还可以求最大最小值,把求和改成max()或者min()即可。

模板:

struct tree
{
    int l,r;
    int s;
}t[400001];
int a[100001];
int p,b,l,r;

int calc(int x,int y)//加和运算,如果求最大最小值,更改这里就可以了。 
{
    return x+y;
}

void buildtree(int id,int l,int r,int *a)
{
    if (l==r)
    {
        t[id].l=l;
        t[id].r=r;
        t[id].s=a[l];
    } else
    {
        int mid=(l+r)/2;
        buildtree(id*2,l,mid,a);
        buildtree(id*2+1,mid+1,r,a);
        t[id].l=l;
        t[id].r=r;
        t[id].s=calc(t[id*2].s,t[id*2+1].s);
    }
}

void update(int id,int s,int w)//s点加上w,可以根据需要改成赋值号 
{
    if (t[id].l==t[id].r)
    {
        t[id].s+=w;
    } else
    {
        int mid=(t[id].l+t[id].r)/2;
        if (s<=mid) update(id*2,s,w);
        else update(id*2+1,s,w);
        t[id].s=calc(t[id*2].s,t[id*2+1].s);
    }
}

int sum(int id,int l,int r)
{
    if ((t[id].l==l)&&(t[id].r==r))
    {
        return t[id].s;
    } else
    {
        int mid=(t[id].l+t[id].r)/2;
        if (r<=mid) return sum(id*2,l,r);
        if (mid<l) return sum(id*2+1,l,r);
        return calc(sum(id*2,l,mid),sum(id*2+1,mid+1,r));
    }
}
原文地址:https://www.cnblogs.com/zhyfzy/p/4293988.html