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;
}
原文地址:https://www.cnblogs.com/kkrisen/p/3255529.html