HDU-4348-主席树区间更新在线

5 篇文章 0 订阅
1 篇文章 0 订阅

!


在这里插入图片描述

题目链接

在这里插入图片描述
在这里插入图片描述

题意

​ 给定区间 q查询l r c l r 更新加d 然后带一个时间戳 加1 然后 h 询问l到r 在t时间戳 b 把时间戳改为t

题意

​ 标准在线主席树啊,树上维护的不是权值线段树,而是对应的正常的树
然后,怎么处理区间更新呢 ,我们都知道,主席树是根据之前的树,来对之前的结点复用也好

总之相当于多一颗树,我们单点修改的好说,只是影响一条链,但是区间更新怎么办

我们可以想到打标记 lazy,但是我们怎么,更新的时候怎么pushup 呢,这个好处理

我们可以发现 我们树从上往下的结构嘛,所以在递归的时候,可以把这个父节点的sum,直接更新

这样省去了pushup 其实也差不多,主要是 怎么处理pushdown 因为你没办法往下传lazy,

这样就会多开结点,相当于你多开了区间这么多结点,直接mle啊

空间爆炸,所以我们要考虑 不pushdown,我们可以从上往下,对树遍历的时候,找完全是你要找的区间

并且在树上有的,就是递归的时候 写三个的那种,

完全是左边r<=mid 就在递归左,完全是右 l>mid 就递归右

然后,中间区间的话,在拆开递归,l到mid,mid+1到r

这样,更新的时候,判断l==L,r==R,这样保证你更新的区间,一定是你要找的区间

然后对应的,我们在更新的时候,sum已经更新完了,加上你更新的值和对区间的影响长度,

即R-L+1,然后,我们呢把完全要找的区间打一个lazy

然后,在查询的时候,我们从上往下的时候,把所有要找的区间lazy算一下,加上去,

这样保证一定是对的,

举个例子 比如 1-10 我更新 1-7 那么 树上影响的区间

1-10
1-5,6-10
6-8
6-7

这几个区间吧,我们在从上往下递归的时候,已经把他们的和都更新过了

然后,我们对于1-5,6-7 这几个区间打了lazy

然后当我们查询的时候,假如找1-4

因为和已经更新过了,所以 1-3的和直接返回就可以

然后找到4-5的时候,接下来递归4,这个时候

正常的操作是 pushdown 标记下传 ,但是我们可以不传标记,

直接把标记的值乘上要找的区间长度 ,加上就可以了

因为你是三个判断的那种递归,保证你找的区间一定是对应要找的长度,所以这样写没有问题

AC代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100000 + 5;
#define ll long long
ll a[N] ;
int rt[N * 35], ls[N * 35], rs[N * 35];
ll lazy[N*35];
ll sum [N * 35];
int tot;
void Build(int &o, int l, int r)
{
    o = ++ tot;
    sum[o] = a[l];
    lazy[o] = 0;
    if(l == r) return;
    int m = (l + r) >> 1;
    Build(ls[o], l, m);
    Build(rs[o], m + 1, r);
    sum[o] = sum[ls[o]] + sum[rs[o]];
}
void update(int &o, int l, int r, int last, int L, int R, ll val)
{
    o = ++ tot;
    ls[o] = ls[last];
    rs[o] = rs[last];
    lazy[o] = lazy[last];
    sum[o] = sum[last]+val*(R-L+1); //把每个区间的增量都算上 这一步省去了pushup
    if(L==l&&r==R)
    {
        lazy[o] += val; //完全是要找区间的并且是完整的一个区间的更新lazy
        return ;
    }
    int m = (l + r) >> 1;
    if(R<=m) update(ls[o],l,m,ls[last],L,R,val);
    else if(L>m) update(rs[o],m+1,r,rs[last],L,R,val);
    else
    {
        update(ls[o],l,m,ls[last],L,m,val);
        update(rs[o],m+1,r,rs[last],m+1,R,val);
    }
}
ll query(int ss, int l, int r, int L,int R)
{
    if(L<=l&&r<=R)
    {
        return sum[ss];
    }
    ll ans = lazy[ss]*(R-L+1); // 每次往下找的时候 把这个区间的lazy对最后和的影响算一下 这一步省去了pushdown
    int m = (l + r) >> 1;
    if(R<=m) ans+=query(ls[ss],l,m,L,R);
    else if(L>m) ans+=query(rs[ss],m+1,r,L,R);
    else
    {
        ans+=query(ls[ss],l,m,L,m);
        ans+=query(rs[ss],m+1,r,m+1,R);
    }
    return ans;
}
int main()
{
    int n,q;
    while(scanf("%d%d", &n, &q)==2)
    {
        for(int i = 1; i <= n; i ++)
        {
            scanf("%lld",&a[i]);
        }
        tot = 0;
        int sz = n;
        Build(rt[0],1, sz);
        int time = 0 ;
        char s[15];
        int ql,qr;
        while(q--)
        {
            scanf("%s",s);
            if(s[0]=='Q')
            {
                scanf("%d%d", &ql, &qr);
                printf("%lld\n",query(rt[time],1,sz,ql,qr));
            }
            else if(s[0]=='C')
            {
                ll val;
                scanf("%d%d%lld", &ql, &qr,&val);
                update(rt[time+1],1,sz,rt[time],ql,qr,val);
                time++;
            }
            else if(s[0]=='H')
            {
                int time1;
                scanf("%d%d%d", &ql, &qr,&time1);
                printf("%lld\n",query(rt[time1],1,sz,ql,qr));
            }
            else if(s[0]=='B')
            {
                scanf("%d",&time);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值