【HDU 4348】 To the moon 可持久化线段树

题目链接:传送门

简述题意:

一个长度为n的数组,4种操作 :

    (1)C l r d:区间[l,r]中的数都加1,同时当前的时间戳加1 。

    (2)Q l r:查询当前时间戳区间[l,r]中所有数的和 。

    (3)H l r t:查询时间戳t区间[l,r]的和 。

    (4)B t:将当前时间戳置为t 。回到过去t时刻,t之后的信息都会消失,即就不会再向前跳跃。

  所有操作均合法 。

                 起始的时间戳为 0.

 

这道题让我明白什么是永久化标记,我做这道题最开始的想法,利用时间戳建树,但是询问和普通线段树一样 lazy 标记,然后pushdown和pushup,结果样例对了,但还是会WA,造了几组样例还是WA(难受。。。),原因在于,向下传递的时候,把原来树的状态给改变了(可以帮助你理解为什么用永久化标记)。

贴一份我的错误代码:https://paste.ubuntu.com/p/rd9yZ7R75z/      

下面是错误的样例.

正确代码:

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<bitset>
#include<set>
#include<stack>
#include<map>
#include<list>
#include<new>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const ll mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=1e5+10;

struct node
{
    int l;
    int r;
    ll sum;
    ll lazy;
} point[4000005];

int root[100005];
int nowt,counts;

int buildtree(int l,int r)
{
    int s=++counts;
    point[s].lazy=0;
    if(l==r)
    {
        scanf("%lld",&point[s].sum);
        return s;
    }
    int mid=(l+r)>>1;
    point[s].l=buildtree(l,mid);
    point[s].r=buildtree(mid+1,r);
    point[s].sum=point[point[s].l].sum+point[point[s].r].sum;
    return s;
}

int updateroot(int left,int l,int r,int a,int b,ll x)
{
    int s=++counts;
    point[s]=point[left];
    point[s].sum+=(b-a+1)*x;
    ///[l,r]区间完全包含[a,b],该区间加上(b-a+1)*x;
    if(l==a&&r==b)
    {
        point[s].lazy+=x;
        ///永久标记:标记整个区间+x,用于询问[l,r]子区间,但是不包括自身,因为上面已经更新区间和了.
        return s;
    }
    int mid=(l+r)>>1;
    if(b<=mid)
        point[s].l=updateroot(point[left].l,l,mid,a,b,x);
    else if(a>mid)
        point[s].r=updateroot(point[left].r,mid+1,r,a,b,x);
    else
    {
        point[s].l=updateroot(point[left].l,l,mid,a,mid,x);
        point[s].r=updateroot(point[left].r,mid+1,r,mid+1,b,x);
    }
    return s;
}
ll find_sum(int t,int l,int r,int a,int b)
{
    if(l==a&&r==b)
        return point[t].sum;
    ll s=(b-a+1)*point[t].lazy;
    ///如果查询的区间是[l,r]的子区间,并且[l,r]被标记过,则需要加上需要增加的值.
    int mid=(l+r)>>1;
    if(b<=mid)
        return find_sum(point[t].l,l,mid,a,b)+s;
    else if(a>mid)
        return find_sum(point[t].r,mid+1,r,a,b)+s;
    else
        return find_sum(point[t].l,l,mid,a,mid)+find_sum(point[t].r,mid+1,r,mid+1,b)+s;
}

int main()
{
    int n,m,l,r,t;
    ll x;
    char c[3];
    scanf("%d %d",&n,&m);
    counts=0;
    nowt=0;
    root[0]=buildtree(1,n);
    while(m--)
    {
        scanf("%s",c);
        if(c[0]=='Q')
        {
            scanf("%d %d",&l,&r);
            printf("%lld\n",find_sum(root[nowt],1,n,l,r));
        }
        else if(c[0]=='H')
        {
            scanf("%d %d %d",&l,&r,&t);
            printf("%lld\n",find_sum(root[t],1,n,l,r));
        }
        else if(c[0]=='C')
        {
            scanf("%d %d %lld",&l,&r,&x);
            root[nowt+1]=updateroot(root[nowt],1,n,l,r,x);
            nowt++;
        }
        else
        {
            scanf("%d",&t);
            nowt=t;
        }
    }
    return 0;
}

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值