线段树简析

线段树资料:http://dongxicheng.org/structure/segment-tree/

PS: 以HDU1166为例

线段树的使用一般分如下几个操作

  1. 建树
  2. 插入
  3. 查询
  • 建树

  建树的操作类似与构建一个二叉树,是一个递归的过程

void build(int l, int r, int step)    
{
    tree[step].left = l;
    tree[step].right = r;
    tree[step].add = 0;
    if (l == r)        //到达叶子节点了
    {
        tree[step].num = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, L(step));
    build(mid+1, r, R(step));
    //其构造是自下而上(先求出其子节点的值),那么其父亲节点很明显就是其左右节点值的和
    tree[step].num = tree[L(step)].num + tree[R(step)].num;
}
  • 插入

  在建立好之后的线段树中,若要是修改一段区间的值时如:[l, r]  代码如下

void insert(int l, int r, int step, int add)
{
    if (tree[step].left == tree[step].right)
    {
        tree[step].num += add;
        return;
    }
    int mid = (tree[step].left + tree[step].right) >> 1;
    if (mid >= l) 
        insert(l, r, L(step), add);
    if (mid < r)
        insert(l, r, R(step), add);
    //由于可能其子节点有更新,我们需要在重新计算一次step这层的值
    tree[step].num = tree[L(step)].num + tree[R(step)].num;
}

  结果正确,但是会超时,因此在这里我们添加了一个标志位,当到达某一层的时候,我们判断一下该层的标志位是否有值,若是有,则修改其左右节点的值,其他的子节点暂时不管,(其实在有些情况下可能根本就不需要用到其子节点的值)代码如下

void update(int step)    //若当前节点的标志位add有值,则更新其左右节点
{
    tree[L(step)].num += tree[step].add * (tree[L(step)].right - tree[L(step)].left + 1);
    tree[L(step)].add += tree[step].add;    //将标志位传给子节点
    tree[R(step)].num += tree[step].add * (tree[R(step)].right - tree[R(step)].left + 1);
    tree[R(step)].add += tree[step].add;
    tree[step].add = 0;                        //父节点清0
}

其完整代码如下

void insert(int l, int r, int step, int add)
{
    if (l<= tree[step].left && tree[step].right <= r)
    {
        tree[step].add += add;    
        tree[step].num += add * (tree[step].right- tree[step].left+ 1);    //add * len
        return;
    }
    if (tree[step].add != 0)    //
    {
        update(step);
    }
    int mid = (tree[step].left + tree[step].right) >> 1;
    if (mid >= l) 
        insert(l, r, L(step), add);
    if (mid < r)
        insert(l, r, R(step), add);
    //由于可能其子节点有更新,我们需要在重新计算一次step这层的值
    tree[step].num = tree[L(step)].num + tree[R(step)].num;
}
  • 查询

  查询的过程和建立有点相似,只是需要返回每一层的结果即可,注意:在查询的时候我们也需要判断其标志位

int query(int l, int r, int step)
{
    int ans = 0;
    if (l <= tree[step].left && tree[step].right <= r)
    {
        return tree[step].num;
    }
    if (tree[step].add != 0)
    {
        update(step);
    }
    int mid = (tree[step].left + tree[step].right) >> 1;
    if (mid >= l)    //与左节点有交集
        ans += query(l, r, L(step));
    if (mid < r)    //与右节点有交集
        ans += query(l, r, R(step));
    return ans;
}

附上HDU1166完整代码如下:

/*
    HDU1166
*/
#include <iostream>
using namespace std;
const int N = 50005;

struct node
{
    int left, right;
    int  num, add;
}tree[4*N];

int L(int n)
{
    return n<<1;
}
int R (int n)
{
    return (n<<1) | 1;
}
void update(int step)    //若当前节点的标志位add有值,则更新其左右节点
{
    tree[L(step)].num += tree[step].add * (tree[L(step)].right- tree[L(step)].left + 1);
    tree[L(step)].add += tree[step].add;    //将标志位传给子节点
    tree[R(step)].num += tree[step].add * (tree[R(step)].right - tree[R(step)].left + 1);
    tree[R(step)].add += tree[step].add;
    tree[step].add = 0;                        //父节点清0
}
int a[N];

void build(int l, int r, int step)    
{
    tree[step].left = l;
    tree[step].right = r;
    tree[step].add = 0;
    if (l == r)        //到达叶子节点了
    {
        tree[step].num = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, L(step));
    build(mid+1, r, R(step));
    //其构造是自下而上(先求出其子节点的值),那么其父亲节点很明显就是其左右节点值的和
    tree[step].num = tree[L(step)].num + tree[R(step)].num;
}

void insert(int l, int r, int step, int add)
{
    if (l<= tree[step].left && tree[step].right <= r)
    {
        tree[step].add += add;    
        tree[step].num += add * (tree[step].right- tree[step].left + 1);    //add * len
        return;
    }
    if (tree[step].add != 0)    //
    {
        update(step);
    }
    int mid = (tree[step].left + tree[step].right) >> 1;
    if (mid >= l) 
        insert(l, r, L(step), add);
    if (mid < r)
        insert(l, r, R(step), add);
    //由于可能其子节点有更新,我们需要在重新计算一次step这层的值
    tree[step].num = tree[L(step)].num + tree[R(step)].num;
}

int query(int l, int r, int step)
{
    int ans = 0;
    if (l <= tree[step].left && tree[step].right <= r)
    {
        return tree[step].num;
    }
    if (tree[step].add != 0)
    {
        update(step);
    }
    int mid = (tree[step].left + tree[step].right) >> 1;
    if (mid >= l)    //与左节点有交集
        ans += query(l, r, L(step));
    if (mid < r)    //与右节点有交集
        ans += query(l, r, R(step));
    return ans;
}

int main()
{
    int t, n, i, k, l, r;
    char str[10];
    scanf("%d", &t);
    for (k=1; k<=t; k++)
    {
        printf("Case %d:\n", k);
        scanf("%d", &n);
        for (i=1; i<=n; i++)
            scanf("%d", &a[i]);
        build(1, n, 1);
        while (scanf("%s", str) && str[0] != 'E')
        {
            scanf("%d%d", &l, &r);
            if (str[0] == 'Q')
            {
                printf("%d\n", query(l, r, 1));
            }
            else 
            {
                r = str[0] == 'S' ? -r : r;
                insert(l, l, 1, r);
            }
        }
    }
    return 0;
}

 

 

 

 

 

posted @ 2013-04-24 10:07  旅行的蜗牛  阅读(245)  评论(0编辑  收藏  举报