zoukankan      html  css  js  c++  java
  • HDU1968 线段树区间更新

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1698

    区间更新的简单思想:

    区间更新是指更新某个区间内的叶子节点的值,因为涉及到的叶子节点不止一个,而叶子节点会影响其相应的非叶父节点,那么回溯需要更新的非叶子节点也会有很多,如果一次性更新完,操作的时间复杂度肯定不是O(lgn),例如当我们要更新区间[0,3]内的叶子节点时,需要更新出了叶子节点3,9外的所有其他节点。为此引入了线段树中的延迟标记概念,这也是线段树的精华所在。

    延迟标记:每个节点新增加一个标记,记录这个节点是否进行了某种修改(这种修改操作会影响其子节点),对于任意区间的修改,我们先按照区间查询的方式将其划分成线段树中的节点,然后修改这些节点的信息,并给这些节点标记上代表这种修改操作的标记。在修改和查询的时候,如果我们到了一个节点p,并且决定考虑其子节点,那么我们就要看节点p是否被标记,如果有,就要按照标记修改其子节点的信息,并且给子节点都标上相同的标记,同时消掉节点p的标记。

    因此需要在线段树结构中加入延迟标记域,本文例子中我们加入标记与addMark,表示节点的子孙节点在原来的值的基础上加上addMark的值,同时还需要修改创建函数build 和 查询函数 query,修改的代码用红色字体表示,其中区间更新的函数为update,


    引用博文链接 http://www.cppblog.com/zhangwangcz/archive/2011/05/04/145697.html

    题意: n个钩子连在一起, 初始重量都为铜 即1, 之后修改 一段区间 的重量为2 或者 3 或者 1

    求总重量

    我的代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 100005;
    
    struct tree
    {
        int left, right, mid;   //区间为[left, right],中间值为mid
        int add;                //加减标记
        int value;              //区间和
        void init()
        {
            //value = 1;
            add = 0;
            mid = (left + right) >> 1;
        }
    };
    
    tree node[N<<2];
    int n, m;           //区间为[1,n],m个询问
    
    inline int LL(int x)
    {
        return x << 1;
    }
    
    inline int RR(int x)
    {
        return x << 1 | 1;
    }
    
    void BuildTree(int left, int right, int x);     //建立线段树,区间[left,right],从节点编号为x处开始
    
    int Query(int left, int right, int x);          //查询区间[left,right]的和,从节点编号为x处开始
    
    void Update(int left, int right, int change, int x);    //区间[left,right]值增加change,从x处开始
    
    void PushUp(int x);     //向上更新
    
    void PushDown(int x);   //向下更新
    
    int main()
    {
        int a, b, c, t, k = 0;
        scanf("%d", &t);
        while(t--)
        {
            scanf("%d %d", &n, &m);
            BuildTree(1, n, 1);
            for (int i = 0; i < m; ++i)
            {
                scanf("%d %d %d", &a, &b, &c);
                Update(a, b, c, 1);
            }
            if(m == 0)
            	printf("Case %d: The total value of the hook is %d.
    ", node[1].value);
            else
            	printf("Case %d: The total value of the hook is %d.
    ", ++k, Query(1, n, 1));
        }
        return 0;
    }
    
    void BuildTree(int left, int right, int x)
    {
        node[x].left = left;
        node[x].right = right;
        node[x].init();
        if (left == right)
        {
            node[x].value = 1;
            return;      //到了最底层节点就直接返回,否则建子树
        }
        BuildTree(left, node[x].mid, LL(x));
        BuildTree(node[x].mid + 1, right, RR(x));
        node[x].value = node[LL(x)].value + node[RR(x)].value;
        return;
    }
    
    int Query(int left, int right, int x)
    {
        if (node[x].add != 0) PushDown(x);      //向下更新
    
        if (node[x].left == left && node[x].right == right)
        {
            return node[x].value;
        }
        if (left > node[x].mid)         //如果查询区间在右子树区间
        {
            return Query(left, right, RR(x));
        }
        else if (right <= node[x].mid)   //如果查询区间在左子树区间
        {
            return Query(left, right, LL(x));
        }
        return Query(left, node[x].mid, LL(x)) + Query(node[x].mid + 1, right, RR(x));  //查询区间跨越左右子树区间的情况
    }
    
    void Update(int left, int right, int change, int x)
    {
    	if(node[x].add == change) return;
        if (node[x].add != 0) PushDown(x);
        if (node[x].left == left && node[x].right == right)
        {
            node[x].add = change;
            node[x].value = change * (right - left + 1);       //有add标记的节点表示该节点value已经被更新但是子节点未更新
            PushUp(x);
            return;
        }
        if (left > node[x].mid)         //如果更新区间在右子树
        {
            Update(left, right, change, RR(x));
        }
        else if (right <= node[x].mid)  //如果更新区间在左子树
        {
            Update(left, right, change, LL(x));
        }
        else        //更新区间跨越左右子树的情况
        {
            Update(left, node[x].mid, change, LL(x));
            Update(node[x].mid + 1, right, change, RR(x));
        }
        return;
    }
    
    void PushUp(int x)
    {
        while (x != 1)
        {
            x >>= 1;
            node[x].value = node[LL(x)].value + node[RR(x)].value;
        }
        return;
    }
    
    void PushDown(int x)
    {
        if (node[x].left == node[x].right)
        {
            node[x].add = 0;
            return;
        }
        node[LL(x)].add = node[x].add;     //更新左子树
        node[LL(x)].value = node[x].add * (node[LL(x)].right - node[LL(x)].left + 1);
    
        node[RR(x)].add = node[x].add;     //更新右子树
        node[RR(x)].value = node[x].add * (node[RR(x)].right - node[RR(x)].left + 1);
        node[x].add = 0;                    //清空父节点
        return;
    }
    

    别人代码,清晰易懂, 时间快

    #include <stdio.h>
    #include <iostream>
    using namespace std;
    
    #define maxn 100005
    struct Tree
    {
        int left, right, num; //num 为 -1,表示这个区间内不纯
    }node[4 * maxn];
    
    void BuildTree(int i, int l, int r);
    
    void Update(int i, int num, int l, int r);
    
    int Search(int i);
    
    int main()
    {
        int t, num, n, m, k = 0, l, r;
        scanf("%d", &t);
        while(t--)
        {
            scanf("%d %d", &n, &m);
            BuildTree(1, 1, n);
            while(m--)
            {
                scanf("%d %d %d", &l, &r, &num);
                Update(1, num, l, r);
            }
            printf("Case %d: The total value of the hook is %d.
    ", ++k, Search(1));
        }
        return 0;
    }
    
    void BuildTree(int i, int l, int r)
    {
        node[i].left = l;
        node[i].right = r;
        node[i].num = 1; //初始化每个区间都是 纯1 的;
        if(node[i].left == node[i].right) return;
    
        int mid = (node[i].left + node[i].right) >> 1;
        BuildTree(i << 1, l, mid);
        BuildTree(i << 1 | 1, mid + 1, r);
    }
    
    void Update(int i, int num, int l, int r)
    {
        if(node[i].num == num) return;
        if(node[i].left == l && node[i].right == r)
        {
            node[i].num = num;
            return;
        }
        if(node[i].num != -1)//这段区间是 纯 的,我们要修改这个区间,则这个区间就不 纯了
        {
            node[i << 1].num = node[i << 1 | 1].num = node[i].num;
            node[i].num = -1;//这个区间不纯了
        }
        int mid = (node[i].left + node[i].right) / 2;
        if(r <= mid) Update(i << 1, num, l, r);
        else if(l > mid) Update(i << 1 | 1, num ,l, r);
        else
        {
            Update(i << 1, num, l, mid);
            Update(i << 1 | 1, num, mid + 1, r);
        }
    }
    int Search(int i)
    {
        if(node[i].num != -1) //即这段区间是纯的, 则不需要向下查询,直接根据线段树的特点算出
        {
            return node[i].num * (node[i].right - node[i].left + 1);
        }
        else
            return Search(i << 1) + Search(i << 1 | 1);
    }

    还有一种更快的方法:判断每个点是否在某更新区间,在则更新,不在不处理,因为前面的会被后面的覆盖,所以我们从后面考虑,倒着做。:

    #include <iostream>
    
    using namespace std;
    
    int data[100005][3];
    
    int main()
    {
        int t,q,n,i,j,sum,k,v;
        scanf("%d",&t);
        for(i=1;i<=t;i++)
        {
            scanf("%d%d",&n,&q);
            for(j=1;j<=q;j++)
                scanf("%d%d%d",&data[j][0],&data[j][1],&data[j][2]);
            sum=0;
            for(k=1;k<=n;k++)
            {
                v=1;
                for(j=q;j>=1;j--)
                    if(data[j][0]<=k && k<=data[j][1])//寻找k所在的更新区间,若存在则更新,不存在v=1不变
                    {
                        v=data[j][2];                 //若找的最后面的更新区间,则停止,因为前面的会被覆盖
                        break;
                    }
                sum+=v;
            }
            printf("Case %d: The total value of the hook is %d.
    ",i,sum);
        }
        return 0;
    }
    


  • 相关阅读:
    WebService-.Net:添加web引用和添加服务引用有什么区别?
    袁氏-人物-科学家:袁隆平(首届国家最高科学技术奖得主、杂交水稻之父)
    术语-BLOB:BLOB
    术语-PM:PM/项目管理 百科
    计算机:SAP (服务访问点(Service Accessing point))
    服务器-Web服务器-Tengine:Tengine 百科
    笔记-Git:Git 笔记
    DB-MDM:MDM/主数据管理 百科
    DB-MD:MD/主数据
    [Ext JS 4] 实战之 带week(星期)的日期选择控件
  • 原文地址:https://www.cnblogs.com/tenlee/p/4420106.html
Copyright © 2011-2022 走看看