HDU 4348 To the moon(主席树 区间更新)题解

题意:

给一个数组A[1] ~ A[n],有4种操作:

Q l r询问l r区间和

C l r v给l r区间每个数加v

H l r t询问第t步操作的时候l r区间和

B t返回到第t步操作

思路:

用主席树维护常规的线段树。我们之前已经知道了主席树单点更新,只要新增一条链就ok了,区间更新也有点差不多。我们每次要更新都用一个lazy标记,但是显然我们不能去更新那些已经存在的区间,那么我们就新建出所有要更新的区间。因为可持久化线段树有些结点不是自己的,所以我们不能直接简单的push up和push down,那么我们在对区间加v的时候处理为:如果这个区间完全是新建的(L <= l && R >= r),那么我直接加lazy标记就好,不是的话就直接更新要更新的区间的那部分 T[now].sum += (min(R, r) - max(L, l) + 1) * v ,然后继续往下更新。我在查询的时候,遇到lazy都要直接加上,因为我这个lazy是不往下传的,所以你往下走的时候要先加上指定的区间的lazy。

代码:

#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include <iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e5 + 10;
const int M = maxn * 30;
const ull seed = 131;
const int INF = 0x3f3f3f3f;
const int MOD = 1000000007;
int a[maxn], root[maxn], tot;
int n, m;
struct node{
    int lson, rson;
    ll sum, lazy;
}T[maxn * 40];
void init(){
    tot = 0;
}
void pushUp(int rt){
    T[rt].sum = T[T[rt].lson].sum + T[T[rt].rson].sum;
}
void build(int l, int r, int &rt){
    rt = ++tot;
    T[rt].lazy = T[rt].sum = 0;
    if(l == r){
        T[rt].sum = a[l];
        return;
    }
    int m = (l + r) >> 1;
    build(l, m,T[rt].lson);
    build(m + 1, r, T[rt].rson);
    pushUp(rt);
}
void update(int l, int r, int L, int R, int &now, int pre, ll v){
    T[++tot] = T[pre], now = tot;
    T[now].sum += (min(R, r) - max(L, l) + 1) * v;
    if(L <= l && R >= r){
        T[now].lazy += v;
        return;
    }
    int m = (l + r) >> 1;
    if(L <= m)
        update(l, m, L, R, T[now].lson, T[pre].lson, v);
    if(R > m)
        update(m + 1, r, L, R, T[now].rson, T[pre].rson, v);
}
ll query(int l, int r, int L, int R, int rt){
    if(L <= l && R >= r){
        return T[rt].sum;
    }
    int m = (l + r) >> 1;
    ll ans = (min(R, r) - max(L, l) + 1) * T[rt].lazy;
    if(L <= m)
        ans += query(l, m, L, R, T[rt].lson);
    if(R > m)
        ans += query(m + 1, r, L, R, T[rt].rson);
    return ans;
}
int main(){
    while(~scanf("%d%d", &n, &m)){
        init();
        for(int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        int time = 0;
        build(1, n, root[time]);
        while(m--){
            char o[3];
            int l, r, v;
            scanf("%s", o);
            if(o[0] == ‘C‘){
                scanf("%d%d%d", &l, &r, &v);
                ++time;
                update(1, n, l, r, root[time], root[time - 1], v);
            }
            else if(o[0] == ‘Q‘){
                scanf("%d%d", &l, &r);
                printf("%lld\n", query(1, n, l, r, root[time]));
            }
            else if(o[0] == ‘H‘){
                    scanf("%d%d%d", &l, &r, &v);
                    printf("%lld\n", query(1, n, l, r, root[v]));
            }
            else{
                scanf("%d", &v);
                time = v;
            }
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/KirinSB/p/10776423.html

时间: 2024-10-04 13:18:39

HDU 4348 To the moon(主席树 区间更新)题解的相关文章

hdu 4348 To the moon(主席树区间操作)

题目链接:hdu 4348 To the moon 题意: 给你n个数,有m个操作. 1.给区间[l,r]的所有数+d,并且时间戳+1 2.询问当前时间戳的区间和. 3.询问过去时间戳t的区间和. 4.退回到时间戳t. 题解: 直接上主席树. 不过区间操作的时候push_down空间似乎不是那么够用. 所有把push_down这个操作去掉. 用一个标记记录当前这个区间的累加. 询问的时候就将这个累加传下去.(具体看代码) 最后还有退回状态t的时候可以把cnt=root[t+1], 因为后面的内存

hdu 4348 To the moon (主席树)

hdu 4348 题意: 一个长度为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 . 所有操作均合法 . 解法: 很明显是一道主席树的题 . 对于每一次区间加法都新建节点建一棵线段树,加个懒惰标记就行了,查询的话直接线段树区间求和 . 不过感觉这一题就是为可持续化数据结构写的,特别是时间戳

HDU 4902 Nice boat(线段树 区间更新)

Nice boat 大意:给你一个区间,每次可以进行两种操作,1:把区间中的数全都变成x  2:把区间中大于x的数变成gcd(a[i], x),最后输出序列. 思路:线段树成段更行,用num数组的叶子存储数据,节点当作lazy来使用. 1 #include <stdio.h> 2 const int maxn = 100005; 3 4 int num[maxn<<2]; 5 6 int gcd(int a, int b){ 7 return b?gcd(b, a%b):a; 8

HDU 1698 Just a Hook (线段树,区间更新)

Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 17214    Accepted Submission(s): 8600 Problem Description In the game of DotA, Pudge’s meat hook is actually the most horrible thing f

hdu 6444 Neko&#39;s loop 线段树区间更新

题目连接:Neko's loop 题意:给一个长度为n的环,下标从0~n-1,环上每个点有个值表示到这个点会得到的快乐值.,然后每次可以花费1能量往后跳k步.你可以选择任意点开始跳,可以任意点结束,最多跳m次问得到至少s的快乐值最初要拥有多少. 题解:先把循环节挑出来,,然后在循环节上找最大字段和.循环节长度为cnt,然后就是枚举起点用线段树维护前缀和,然后取最大值. #include<bits/stdc++.h> #define ls l,m,rt<<1 #define rs m

HDU 1698 Just a Hook (线段树 区间更新基础)

Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 21856    Accepted Submission(s): 10963 Problem Description In the game of DotA, Pudge's meat hook is actually the most horrible thing

HDU 1689 Just a Hook 线段树区间更新求和

点击打开链接 Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 18894    Accepted Submission(s): 9483 Problem Description In the game of DotA, Pudge's meat hook is actually the most horrible

(简单) HDU 1698 Just a Hook , 线段树+区间更新。

Description: In the game of DotA, Pudge’s meat hook is actually the most horrible thing for most of the heroes. The hook is made up of several consecutive metallic sticks which are of the same length. Now Pudge wants to do some operations on the hook

HDU - 4348 To the moon(主席树区间更新)

题目链接:To the moon 题意:给个数组,三种操作,第一种询问当前区间[l,r]的和,第二种给区间[l,r]的每一个数加上d,第三种询问在第几次修改后[l,r]的权值 题解:如果这题只询问区间和更新,简单建棵线段树维护区间和用延时标记就可以了,但是它询问第几次修改之后一段区间的值,这样的话刚才的方法就不好用,那我们可不可以,在每次修改之后的重新建一棵线段树呢?直接重新建的话空间会爆,这个时候就可以用??币的主席树(没学过主席树可以先做一下这个),每次的修改只会再新增logN个节点,先把给