线段树 标记永久化

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Apale_8/article/details/82118574

一般线段树做区间修改操作时,先是找到目标区间,然后修改该区间,并打下延迟标记,最后从目标区间自底向上,更新所有包含目标区间的区间的值(即pushup)。当该区间子节点被访问前,pushdown下推标记。
这种维护区间的方式存在一点点弊端。例如用这种方式写一棵可持久化线段树,因为每次pushdown都相当于继续之前延迟了的更新操作,所以得开新的节点,然后开着开着可能就爆内存了…..

一种更好的方法就是用标记永久化。 从字面意思就可以知道,永久化的标记是不会变的,这个不变是不会变小(一般的那种 pushdown的时候标记都清0了)。

实现过程:update[L,R]时,把所有包含[L,R]的区间更新(显然修改[L,R]能影响到的就是这些区间),然后在[L,R]上打上标记;query[L,R]时,自顶向下找区间[L,R],统计从根到目标节点的路上标记的和,结果就是目标区间的值加上路径上所有标记的影响。
优势:不用pushdown,代码短了,且可持久化的时候不用开新的节点;与下推标记的写法相比略快一点
下面附上poj3468和hdu4348的代码
poj3468

#include<cstdio>
#include<cstring>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 100000+10;
typedef long long ll;
ll t[maxn<<2],lazy[maxn<<2];

void build(int l,int r,int rt)
{
    lazy[rt] = 0;
    if(l==r)
    {
        scanf("%lld",&t[rt]);
        return ;
    }
    int m = l+r >> 1;
    build(lson);
    build(rson);
    t[rt] = t[rt<<1] + t[rt<<1|1];
}

void update(int L,int R,int val,int l,int r,int rt)
{
    t[rt] += (ll)(R-L+1) * val;
    if(L==l&&r==R)
    {
        lazy[rt] += val;
        return ;
    }
    int m = l + r >> 1;
    if(R<=m)
        update(L,R,val,lson);
    else if(L>m)
        update(L,R,val,rson);
    else update(L,m,val,lson), update(m+1,R,val,rson);
}

ll query(int L,int R,int l,int r,int rt,int add)
{
    if(L==l&&r==R)
        return t[rt] + (ll)add*(r-l+1);
    int m = l + r >> 1;
    if(R<=m)
        return query(L,R,lson,add+lazy[rt]);
    else if(L>m)
        return query(L,R,rson,add+lazy[rt]);
    else return query(L,m,lson,add+lazy[rt]) + query(m+1,R,rson,add+lazy[rt]);
}

int N,Q;
int x,y;
ll z;
char str[10];
int main()
{
    scanf("%d%d",&N,&Q);
    build(1,N,1);
    while(Q--)
    {
        scanf("%s",str);
        if(str[0]=='C')
        {
            scanf("%d%d%d",&x,&y,&z);
            update(x,y,z,1,N,1);
        }


        else
        {
            scanf("%d%d",&x,&y);
            printf("%lld\n",query(x,y,1,N,1,0));
        }
    }
    return 0;
}

hdu4348

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1e5 + 5;
typedef long long ll;
int lazy[maxn*30],ls[maxn*30],rs[maxn*30],N,Q;
ll t[maxn*30];
int tot,root[maxn],nowroot;
#define lson l,m,ls[now]
#define rson m+1,r,rs[now]
void build(int l,int r,int &now)
{
    now = ++tot;
    lazy[now] = 0;
    if(l==r)
    {
        scanf("%I64d",&t[now]);
        //printf("t[%d] = %lld\n",now,t[now]);
        return ;
    }
    int m = l + r >> 1;
    build(lson);
    build(rson);
    t[now] = t[ls[now]] + t[rs[now]];
    //printf("t[%d] = %lld\n",now,t[now]);
}

void update(int L,int R,int val,int l,int r,int &now,int lst)
{
    now = ++tot;
    ls[now] = ls[lst], rs[now] = rs[lst];
    t[now] = t[lst] + (ll)(R-L+1)*val;
    lazy[now] = lazy[lst];
    if(L == l && r == R)
    {
        lazy[now] += val;
        return ;
    }
    int m = l + r >> 1;
    if(R<=m)
        update(L,R,val,lson,ls[lst]);
    else if(L>m)
        update(L,R,val,rson,rs[lst]);
    else
        update(L,m,val,lson,ls[lst]), update(m+1,R,val,rson,rs[lst]);
}

ll query(int L,int R,int l,int r,int now,int add)
{
    if(L == l && r == R)
        return t[now] + (ll)(R-L+1)*add;
    int m = l + r >> 1;
    if(R<=m)
        return query(L,R,lson,add+lazy[now]);
    else if(L>m)
        return query(L,R,rson,add+lazy[now]);
    else
        return query(L,m,lson,add+lazy[now]) + query(m+1,R,rson,add+lazy[now]);
}

void init()
{
    tot = 0;
    nowroot = 1;
}

int main()
{
    char op[2];
    int l,r,v;
    while(scanf("%d%d",&N,&Q)!=EOF)
    {
        init();
        build(1,N,root[1]);
        while(Q--)
        {
            scanf("%s",op);
            switch(op[0])
            {
            case 'Q':
                scanf("%d%d",&l,&r);
                printf("%I64d\n",query(l,r,1,N,root[nowroot],0));
                break;
            case 'C':
                scanf("%d%d%d",&l,&r,&v);
                update(l,r,v,1,N,root[nowroot+1],root[nowroot]);
                ++nowroot;
                break;
            case 'H':
                scanf("%d%d%d",&l,&r,&v);
                printf("%I64d\n",query(l,r,1,N,root[v+1],0));
                break;
            default:
                scanf("%d",&v);
                tot = root[v+2] - 1;//回收内存
                nowroot = v+1;
                break;
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/Apale_8/article/details/82118574