zoukankan      html  css  js  c++  java
  • POJ 3468 线段树

    终于线段树还是有所领悟,发现对一个算法真的是,一开始云里雾里,后来这些天狂搞图论,数据结构,把KMP,Manacher,最短路,最小生成树 都狂弄了一下之后,发现再来弄线段树就容易多了,至少建树,更新什么的,我脑海里已经可以自己构图了。。所以,一个心得是,算法可以让脑子变的灵活聪明,前几天看的云里雾里的东西,过几天就会有领悟了。

    好了,进入正题。

    挑了一道还算可以的线段树题目,上午A了一道,完全是纯线段树水题,这题涉及懒惰标记,感觉高端了好多。。就算用了懒惰标记,仍然跑了将近3000MS,话说最近做图论题目发现,G++总是比C++要多几百MS(数据量本身比较大的时候)。。。而且尤其碰到几个坑题,用G++就WA,用C++就A。。

    这个题,尼玛也坑爆了,看了discuss,发现居然要把很多变量设置成long long,但是涉及数据的比较,可能出问题,后来一气之下,全部改成了long long,还有个坑的地方是其增量可以为负数,。。这个也怪我,其实题目里写C的范围的时候已经写明了。

    线段树的写法其实理解了都很简单,边写心里就已经建好了图(也应该是由于我现在太菜了,只在弄弄简单线段树)。

    懒惰标记是一个比较难想的地方,而且容易出问题,还需细细消化。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define maxn 100005
    #define Lson (x<<1),l,mid //线段树访问左右孩子,直接在这里定义,方便下面调用,这个东西实在重用太多了
    #define Rson (x<<1|1),mid+1,r
    using namespace std;
    long long d[maxn*5];
    long long add[maxn*5];
    void getup(long long x)//更新父节点操作,依据具体题目的要求来决定
    {
        d[x]=d[x<<1]+d[x<<1|1];
    }
    void getdown(long long x,long long ln,long long rn) //向下更新子节点,这也是懒惰标记得以实现的重要函数。
    {
        d[x<<1]+=add[x]*ln; //节点之下还有多少子节点,就必须加多少。
        d[x<<1|1]+=add[x]*rn;
        if (ln>1) add[x<<1]+=add[x];
        if (rn>1) add[x<<1|1]+=add[x];
    
        add[x]=0; //x的子节点已经更新完毕,务必将其设置为0.
    }
    void build(long long x,long long l,long long r)//很经典的建树操作
    {
        if (l==r)
        {
            scanf("%lld",&d[x]);
            return;
        }
        long long mid=(l+r)/2;
        build(Lson);
        build(Rson);
        getup(x);
    } //通过建树操作,发现线段树的建树就是从最低层的节点开始,层层上推,使得每个节点段都能将该节点以下的所有点信息全部囊括。
    void update(long long L,long long R,long long A,long long x,long long l,long long r)//节点更新操作。
    {
        if (L<=l&&R>=r)//因为本题是进行段更新,因此这里写法类似于询问操作
        {
            d[x]+=(r-l+1)*A;
            if (r-l>0) add[x]+=(long long)A; //懒惰标记,表示该节点的子节点需要更新,但不是在本次,下次有访问需求才会更新。。大大节省时间
            return;
        }
        long long mid=(l+r)/2;
        if (add[x]!=0)//由于增量可能为负数,所以只能!=0
            getdown(x,mid-l+1,r-mid); //当访问到了懒惰标记的节点,才会进行更新
        if (L<=mid)
            update(L,R,A,Lson);
    
        if (R>mid)
            update(L,R,A,Rson);
        getup(x);//更新父节点
    
    }
    long long query(long long L,long long R,long long x,long long l,long long r)
    {
        if (L<=l&&R>=r)
        {
            return d[x];
        }
        long long ret=0;
        long long mid=(l+r)/2;
    
        if (add[x]!=0)
        {
            getdown(x,mid-l+1,r-mid);
        }
        if (L<=mid) ret+=query(L,R,Lson);//询问操作,递归到子节点去,通过子节点的返回值来最终组合为这一段的值。
        if (R>mid) ret+=query(L,R,Rson);
        return ret;
    }
    int main()
    {
        long long n,q;
        while (scanf("%lld %lld",&n,&q)!=EOF)
        {
            memset(add,0,sizeof add);
            build(1,1,n);
            int i,j;
            for (i=1; i<=q; i++)
            {
    
                char ch;
                long long a,b,c;
                scanf("
    %c",&ch);
                if (ch=='Q')
                {
                    scanf("%lld %lld",&a,&b);
                    printf("%lld
    ",query(a,b,1,1,n));
                }
                if (ch=='C')
                {
                    scanf("%lld %lld %lld",&a,&b,&c);
                    update(a,b,c,1,1,n);
                }
            }
        }
        return 0;
    }
  • 相关阅读:
    JavaWeb工程中url地址的写法
    java.lang.NoClassDefFoundError
    {"aa":null} 如何能转化为 {"aa":{}}
    IDEA遇到项目包后面出现% classes,% lines covered该怎么解决
    union all 关键字的应用(合并两个查询结果集到同一个结果集)
    IDEA中的,让光标回到上一次停留的地方
    克隆指定的分支:git clone -b 分支名仓库地址
    转载:再难也要向前爬-唐雅薇
    MacBook Pro 新手入门
    转载:不是书评 :《我是一只IT小小鸟》
  • 原文地址:https://www.cnblogs.com/kkrisen/p/3255529.html
Copyright © 2011-2022 走看看