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 。

  所有操作均合法 。

解法:

  很明显是一道主席树的题 。

  对于每一次区间加法都新建节点建一棵线段树,加个懒惰标记就行了,查询的话直接线段树区间求和 。

  不过感觉这一题就是为可持续化数据结构写的,特别是时间戳这一点,当前时间戳的版本就是从上一个时间戳继承下来的,而且所有记录都保存了下来,好神奇,感觉对主席树的理解又加深了一点 。

code 主席树

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <cmath>
  5 #include <cstring>
  6 #include <queue>
  7 #include <set>
  8 #include <vector>
  9 #include <map>
 10 #define ll long long
 11
 12 using namespace std;
 13
 14 const int N=100000+7;
 15
 16 int root[N],tot;
 17 int Ls[N*30],Rs[N*30],add[N*30];
 18 ll sum[N*30];
 19
 20 int n,m;
 21
 22 inline int bulidtree(int L,int R){
 23     int k=tot++;
 24     add[k]=0;
 25
 26     if (L==R){
 27         scanf("%lld",&sum[k]);
 28         return k;
 29     }
 30
 31     int mid=(L+R)>>1;
 32     Ls[k]=bulidtree(L,mid);
 33     Rs[k]=bulidtree(mid+1,R);
 34
 35     sum[k]=sum[Ls[k]]+sum[Rs[k]];
 36
 37     return k;
 38 }
 39
 40 inline int update(int o,int L,int R,int x,int LL,int RR){
 41     int k=tot++;
 42     Ls[k]=Ls[o]; Rs[k]=Rs[o]; add[k]=add[o]; sum[k]=sum[o];
 43
 44     sum[k]+=(ll)x*(R-L+1);
 45
 46     if (LL==L && RR==R){
 47         add[k]+=x;
 48         return k;
 49     }
 50
 51     int mid=(LL+RR)>>1;
 52     if (R<=mid) Ls[k]=update(Ls[k],L,R,x,LL,mid);
 53     else if (L>mid) Rs[k]=update(Rs[k],L,R,x,mid+1,RR);
 54     else {
 55         Ls[k]=update(Ls[k],L,mid,x,LL,mid);
 56         Rs[k]=update(Rs[k],mid+1,R,x,mid+1,RR);
 57     }
 58
 59     return k;
 60 }
 61
 62 inline ll query(int o,int L,int R,int LL,int RR){
 63     if (L==LL && R==RR) return sum[o];
 64
 65     int mid=(LL+RR)>>1;
 66
 67     ll ret=(ll)add[o]*(R-L+1);
 68
 69     if (R<=mid) return ret+query(Ls[o],L,R,LL,mid);
 70     else if (L>mid) return ret+query(Rs[o],L,R,mid+1,RR);
 71     else return ret+query(Ls[o],L,mid,LL,mid)+query(Rs[o],mid+1,R,mid+1,RR);
 72 }
 73
 74 int main(){
 75     int x,L,R;
 76     int now;
 77     char ch[3];
 78
 79     bool f=false;
 80
 81     while (~scanf("%d%d",&n,&m)){
 82         if (f) puts("");
 83         else f=true;
 84
 85         tot=0;
 86         root[0]=bulidtree(1,n);
 87         now=0;
 88
 89         while (m--){
 90             scanf("%s",ch);
 91             if (ch[0]==‘C‘) {
 92                 scanf("%d%d%d",&L,&R,&x);
 93                 now++;
 94                 root[now]=update(root[now-1],L,R,x,1,n);
 95             }
 96             else if (ch[0]==‘Q‘) {
 97                 scanf("%d%d",&L,&R);
 98                 printf("%lld\n",query(root[now],L,R,1,n));
 99             }
100             else if (ch[0]==‘H‘){
101                 scanf("%d%d%d",&L,&R,&x);
102                 printf("%lld\n",query(root[x],L,R,1,n));
103             }
104             else if (ch[0]==‘B‘) {
105                 scanf("%d",&now);
106             }
107         }
108     }
109
110     return 0;
111 }
时间: 2025-01-03 16:37:06

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 4866 多校1 主席树+扫描线

终于是解决了这个题目了 不过不知道下一次碰到主席树到底做不做的出来,这个东西稍微难一点就不一定能做得出 离散化+扫描线式的建树,所以对于某个坐标二分找到对应的那颗主席树,即搜索出结果即可(因为是扫描线式的建树,找到对应的树之后,就知道该点上面的线段有多少条了) 其他就是普通主席树的操作了 主席树里面维护两个东西,一个就是普通的那种在该区间的节点数目,另外就是权值 #include <iostream> #include <cstdio> #include <cstring&g

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

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

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和pu

HDU 4417 Super Mario(主席树求区间内的区间查询+离散化)

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5101    Accepted Submission(s): 2339 Problem Description Mario is world-famous plumber. His “burly” figure and amazing jumping abilit

POJ 2104&amp;HDU 2665 Kth number(主席树入门+离散化)

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 50247   Accepted: 17101 Case Time Limit: 2000MS Description You are working for Macrohard company in data structures department. After failing your previous task about key inse

hdu 4417 Super Mario (主席树)

链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417 题意: 给你段长为n的序列,有q个询问,每次询问区间[l.r]内有多少个数小于等于k 思路: 之前用分块写过类似的,不过为了练习下主席树,这里用主席树写了下.思路很简单 离线离散化处理下,每次插入一个数num时,在主席树上下标num+1,这样每次询问[l,r]中有多少个小于k的数的时候,我们只要找下标[1,k]的区间第R次修改后的总和减去第L-1次修改后的总值就可以得到了 实现代码: #inclu

HDU 5919 -- Sequence II (主席树)

题意: 给一串数字,每个数字的位置是这个数第一次出现的位置. 每个询问对于序列的一个子区间,设一共有k个不同的数,求第ceil(k/2)个数的位置. 因为强制在线,所以离线乱搞pass掉. 主席树可解. 考虑一个数列: p      1 2 3 4 5 6  // 原序列标号 a : 1 2 1 2 3 4  // 原序列 p1 1 2 1 2 5 6  // 子序列开始下标为1 p2        2 3 1 5 6 p3           3 4 5 6 p4              4

HDU 2665 &amp;&amp; POJ 2104(主席树)

http://poj.org/problem?id=2104 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <string> 5 #include <cmath> 6 #include <iostream> 7 #include <stack> 8 using namespace std; 9 #define N 100