HDU4348:To the moon

浅谈主席树:https://www.cnblogs.com/AKMer/p/9956734.html

浅谈标记永久化:https://www.cnblogs.com/AKMer/p/10137227.html

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4348

相比一般的主席树,这个要多支持一个操作,那就是区间修改。我们不能把区间里的每一条链都建出来,因为那样时间空间都不允许,所以我们可以依靠标记永久化来实现。每次询问的时候,把一路上的标记全部累加起来,再计算对当前区间答案的影响就好了。

时间复杂度:\(O((n+m)logn)\)

空间复杂度:\(O((n+m)logn)\)

代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;

const int maxn=1e5+5;

char s[10];
int n,m,tim;
int a[maxn],rt[maxn];

int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}

struct tree_node {
    ll sum;
    int ls,rs,tag;
};

struct Chairman_tree {
    int tot;
    tree_node tree[maxn*30];

    void updata(int p) {
        tree[p].sum=tree[tree[p].ls].sum+tree[tree[p].rs].sum;
    }

    void build(int &now,int l,int r) {
        now=++tot;tree[now].tag=0;
        if(l==r) {
            tree[now].sum=a[l];
            tree[now].ls=tree[now].rs=0;
            return;
        }
        int mid=(l+r)>>1;
        build(tree[now].ls,l,mid);
        build(tree[now].rs,mid+1,r);
        updata(now);
    }

    void change(int lst,int &now,int l,int r,int L,int R,int v) {
        now=++tot;tree[now]=tree[lst];
        tree[now].sum+=1ll*(min(r,R)-max(l,L)+1)*v;//记得要一路修改下去
        //如果调用updata函数用儿子节点更新自己的值,可能会用没被标记影响的上一个版本的主席树的儿子来更新自己
        if(L<=l&&r<=R) {
            tree[now].tag+=v;
            return;
        }
        int mid=(l+r)>>1;
        if(L<=mid)change(tree[lst].ls,tree[now].ls,l,mid,L,R,v);
        if(R>mid)change(tree[lst].rs,tree[now].rs,mid+1,r,L,R,v);
    }

    ll query(int now,int l,int r,int L,int R,int res) {
        if(L<=l&&r<=R)return tree[now].sum+1ll*(r-l+1)*res;
        int mid=(l+r)>>1;ll val=0;
        if(L<=mid)val+=query(tree[now].ls,l,mid,L,R,res+tree[now].tag);
        if(R>mid)val+=query(tree[now].rs,mid+1,r,L,R,res+tree[now].tag);
        return val;
    }
}T;

int main() {
    while(~scanf("%d%d",&n,&m)) {
        tim=T.tot=0;
        for(int i=1;i<=n;i++)
            a[i]=read();
        T.build(rt[0],1,n);
        for(int i=1;i<=m;i++) {
            scanf("%s",s+1);
            if(s[1]=='C') {
                tim++;int l=read(),r=read(),d=read();
                T.change(rt[tim-1],rt[tim],1,n,l,r,d);
            }
            else if(s[1]=='Q') {
                int l=read(),r=read();
                ll ans=T.query(rt[tim],1,n,l,r,0);
                printf("%lld\n",ans);
            }
            else if(s[1]=='H') {
                int l=read(),r=read(),t=read();
                ll ans=T.query(rt[t],1,n,l,r,0);
                printf("%lld\n",ans);
            }
            else if(s[1]=='B') {
                int t=read();
                tim=t;T.tot=rt[tim+1];
            }
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/AKMer/p/9967587.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值