To the moon(主席树)两种方法带你完成哟

题目链接1(内存限制64MB,时间限制2s)
题目链接2(内存限制1536MB,时间限制1s)
上面两个题目一样,但是条件限制不同。
题意:给一串数字。有四种不同的操作。

  • Q L R 查询当前[L,R]的数字之和
  • H L R T 查询在T时[L,R]的数字之和
  • C L R X 使[L,R]所有的数字加上X,并且+1s
  • B T 回到T时刻

解题思路:
如果我们用可持续化线段树去完成这道题。最大的难题是如何保证后来生成的树不会对以前的树产生影响。MOGU目前只知道两种方法。
如果是因为三操作生成的树,如果会对前面的已经存在的旧树产生影响,那么就是pushdown操作。所以我们的思考重点可以放在pushdown上面。
方法一:判断pushdown是,该节点的子节点是否是完全新建的,还是在老树上的。如果是在老树上,那么就新建一个。该方法容易想到,但是对内存的消耗很大。上面的链接只能过掉题目链接2.

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn=1E6+5;
int rt[maxn<<4];
int root_num;
struct node{
    LL val,lazy;
    int ls,rs;
    int mark;
}tree[maxn<<4];

void push_up(int root){
    tree[root].val=tree[tree[root].ls].val+tree[tree[root].rs].val;
}

int build_tree(int l,int r){
    int t=++root_num;
    tree[t].val=tree[t].mark=tree[t].lazy=0;
    if (l==r){
        scanf("%lld",&tree[t].val);
        tree[t].ls=tree[t].rs=0;
        return t;
    }
    int mid=(l+r)>>1;
    tree[t].ls=build_tree(l,mid);
    tree[t].rs=build_tree(mid+1,r);
    push_up(t);
    return t;
}

void push_down(int l,int r,int root){
    if (tree[root].mark){
        int ls=++root_num;
        int rs=++root_num;
        tree[ls]=tree[tree[root].ls];
        tree[rs]=tree[tree[root].rs];
        tree[root].ls=ls,tree[root].rs=rs;
        tree[ls].mark=tree[rs].mark=1;
        tree[root].mark=0;
    }
    if (tree[root].lazy) {
        int ls=tree[root].ls,rs=tree[root].rs;
        int mid=(l+r)>>1;
        tree[ls].val+=tree[root].lazy*(mid-l+1);
        tree[rs].val+=tree[root].lazy*(r-mid);
        tree[rs].lazy+=tree[root].lazy;
        tree[ls].lazy+=tree[root].lazy;
        tree[root].lazy=0;
    }
}

int update(int l,int r,int ql,int qr,int root,int val){
    int t=++root_num;
    if (l==ql&&r==qr){
        tree[t]=tree[root];
        tree[t].val+=val*(r-l+1);
        tree[t].lazy+=val;
        tree[t].mark=l==r?0:1;
        return t;
    }
    int mid=(l+r)>>1;
    tree[t].mark=tree[t].val=tree[t].lazy=0;
    push_down(l,r,root);
    if (qr<=mid) {
        tree[t].ls=update(l,mid,ql,qr,tree[root].ls,val);
        tree[t].rs=tree[root].rs;
    }
    else if (ql>mid) {
        tree[t].ls=tree[root].ls;
        tree[t].rs=update(mid+1,r,ql,qr,tree[root].rs,val);
    }
    else {
        tree[t].ls=update(l,mid,ql,mid,tree[root].ls,val);
        tree[t].rs=update(mid+1,r,mid+1,qr,tree[root].rs,val);
    }
    push_up(t);
    return t;
}

LL query(int l,int r,int ql,int qr,int root){
    if (l==ql&&r==qr){
        return tree[root].val;
    }
    int mid=(l+r)>>1;
    push_down(l,r,root);
    if (qr<=mid) return query(l,mid,ql,qr,tree[root].ls);
    else if (ql>mid) return query(mid+1,r,ql,qr,tree[root].rs);
    else return query(l,mid,ql,mid,tree[root].ls)+query(mid+1,r,mid+1,qr,tree[root].rs);
}

int main (){
    int n,m;
    while (~scanf("%d%d",&n,&m)){
        int cnt=0;
        root_num=0;
        rt[cnt]=build_tree(1,n);
        for (int i=1;i<=m;i++){
            char str[10];
            int l,r,x;
            scanf("%s",str);
            if (str[0]=='C'){
                scanf("%d%d%d",&l,&r,&x);
                cnt++;
                rt[cnt]=update(1,n,l,r,rt[cnt-1],x);
            }
            if (str[0]=='Q'){
                scanf("%d%d",&l,&r);
                printf("%lld\n",query(1,n,l,r,rt[cnt]));
            }
            if (str[0]=='H'){
                scanf("%d%d%d",&l,&r,&x);
                printf("%lld\n",query(1,n,l,r,rt[x]));
            }
            if (str[0]=='B'){
                scanf("%d",&x);
                cnt=x;
            }
        }
        puts("");
    }
    return 0;
}

方法二:可以不用pushdown操作,那么我们就可以纪录每次 从根节点到我们要求的区间所遇见的所有lazy。因为我们从根节点到要查询的区间,碰到的每一个区间都是我们要求的区间的父区间。换而言之,我们不用pushdown操作,把lazy推下去,但是我们可以在二分查找目标区间时,纪录碰到的lazy。最后起到的效果是一样的。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define LL long long
#define MT(a,b) memset(a,b,sizeof(a))
const int mod=10007;
const int maxn=1e5+5;
const int ONF=-0x3f3f3f3f;
const int INF=0x3f3f3f3f;
LL root_num;
int rt[maxn];
struct node{
    LL val,lazy;
    int ls,rs;
}tree[maxn<<5];
void push_up(int root){tree[root].val=tree[tree[root].ls].val+tree[tree[root].rs].val;}
int build_tree(int l,int r){
    int t=++root_num;
    tree[t].val=tree[t].lazy=0;
    if (l==r){
        scanf("%lld",&tree[t]);
        tree[t].ls=tree[t].rs=0;
        return t;
    }
    int mid=(l+r)>>1;
    tree[t].ls=build_tree(l,mid);
    tree[t].rs=build_tree(mid+1,r);
    push_up(t);
    return t;
}

int update(int l,int r,int ql,int qr,int val,int root,LL res){
    int t=++root_num;
    tree[t]=tree[root];
    if (l==ql&&r==qr){
        tree[t].lazy+=val;
        return t;
    }
    tree[t].val += val*(qr-ql+1);
    int mid=(l+r)>>1;
    res+=tree[root].lazy;
    if (qr<=mid) {
        tree[t].ls=update(l,mid,ql,qr,val,tree[root].ls,res);
        tree[t].rs=tree[root].rs;
    }
    else if (ql>mid) {
        tree[t].ls=tree[root].ls;
        tree[t].rs=update(mid+1,r,ql,qr,val,tree[root].rs,res);
    }
    else{
        tree[t].ls=update(l,mid,ql,mid,val,tree[root].ls,res);
        tree[t].rs=update(mid+1,r,mid+1,qr,val,tree[root].rs,res);
    }
    return t;
}

LL query(int l,int r,int ql,int qr,int root,LL res){
    res+=tree[root].lazy;
    if (l==ql&&r==qr){
        return tree[root].val+res*(r-l+1);
    }
    int mid=(l+r)>>1;
    if (qr<=mid) return query(l,mid,ql,qr,tree[root].ls,res);
    else if (ql>mid ) return query(mid+1,r,ql,qr,tree[root].rs,res);
    else return query(l,mid,ql,mid,tree[root].ls,res)+query(mid+1,r,mid+1,qr,tree[root].rs,res);
}

int main (){
    LL n,m;
    while (~scanf("%lld%lld",&n,&m)){
        int cnt=0;
        root_num=0;
        rt[cnt]=build_tree(1,n);
        char str[5];
        while (m--){
            int st,ed,x;
            scanf("%s",str);
            if (str[0]=='C'){
                scanf("%d%d%d",&st,&ed,&x);
                cnt++;
                rt[cnt]=update(1,n,st,ed,x,rt[cnt-1],0);
            }
            if (str[0]=='Q'){
                scanf("%d%d",&st,&ed);
                printf("%lld\n",query(1,n,st,ed,rt[cnt],0));
            }
            if (str[0]=='H'){
                scanf("%d%d%d",&st,&ed,&x);
                printf("%lld\n",query(1,n,st,ed,rt[x],0));
            }
            if (str[0]=='B'){
                scanf("%d",&x);cnt=x;
            }
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值