[HDU4348]To the moon(主席树+标记永久化)

学可持久化treap的时候才发现自己竟然没写过需要标记下传的主席树,然而现在发现大部分操作都可以标记永久化,下传会增大占用空间。

这题一种写法是和普通的线段树一样标记下传,注意所有修改操作(包括put())都要新建点。于是MLE了。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define lson v[x].ls,L,mid
 4 #define rson v[x].rs,mid+1,R
 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 6 typedef long long ll;
 7 using namespace std;
 8
 9 const int N=100010;
10 char op;
11 int n,m,l,r,k,tim,nd,a[N],rt[N];
12 struct Tr{ int ls,rs; ll sm,tag; }v[N*25];
13
14 void put(int &x,int L,int R,ll k){ if (x) v[++nd]=v[x],x=nd,v[nd].sm+=(R-L+1)*k,v[nd].tag+=k; }
15
16 void push(int x,int L,int R){ int mid=(L+R)>>1; if (v[x].tag) put(lson,v[x].tag),put(rson,v[x].tag),v[x].tag=0; }
17
18 void build(int &x,int L,int R){
19     x=++nd;
20     if (L==R){ v[x]=(Tr){0,0,a[L],0}; return; }
21     int mid=(L+R)>>1;
22     build(lson); build(rson);
23     v[x].sm=v[v[x].ls].sm+v[v[x].rs].sm; v[x].tag=0;
24 }
25
26 void ins(int y,int &x,int L,int R,int l,int r,int k){
27     x=++nd; v[x]=v[y];
28     if (L==l && r==R){ v[x].sm+=1ll*(R-L+1)*k; v[x].tag+=k; return; }
29     int mid=(L+R)>>1; push(x,L,R);
30     if (r<=mid) ins(v[y].ls,lson,l,r,k);
31     else if (l>mid) ins(v[y].rs,rson,l,r,k);
32         else ins(v[y].ls,lson,l,mid,k),ins(v[y].rs,rson,mid+1,r,k);
33     v[x].sm=v[v[x].ls].sm+v[v[x].rs].sm;
34 }
35
36 ll que(int x,int L,int R,int l,int r){
37     if (L==l && r==R) return v[x].sm;
38     int mid=(L+R)>>1; push(x,L,R);
39     if (r<=mid) return que(lson,l,r);
40     else if (l>mid) return que(rson,l,r);
41         else return que(lson,l,mid)+que(rson,mid+1,r);
42 }
43
44 int main(){
45     freopen("hdu4348.in","r",stdin);
46     freopen("hdu4348.out","w",stdout);
47     while (~scanf("%d%d",&n,&m)){
48         rep(i,1,n) scanf("%d",&a[i]);
49         nd=tim=0; build(rt[0],1,n);
50         rep(i,1,m){
51             scanf(" %c",&op);
52             if (op==‘C‘) scanf("%d%d%d",&l,&r,&k),tim++,ins(rt[tim-1],rt[tim],1,n,l,r,k);
53             if (op==‘Q‘) scanf("%d%d",&l,&r),printf("%lld\n",que(rt[tim],1,n,l,r));
54             if (op==‘H‘) scanf("%d%d%d",&l,&r,&k),printf("%lld\n",que(rt[k],1,n,l,r));
55             if (op==‘B‘) scanf("%d",&k),tim=k;
56         }
57         puts("");
58     }
59     return 0;
60 }

未永久化(MLE)

另一种写法就是标记永久化,若一个修改区间覆盖当前区间则将tag+=k,但并不下传。询问时将答案加上tag的贡献即可。

注意多组数据的清空问题。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define lson v[x].ls,L,mid
 4 #define rson v[x].rs,mid+1,R
 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 6 typedef long long ll;
 7 using namespace std;
 8
 9 const int N=100010;
10 char op;
11 int n,m,l,r,k,tim,nd,flag,a[N],rt[N];
12 struct Tr{ int ls,rs; ll sm,tag; }v[N*25];
13
14 void build(int &x,int L,int R){
15     x=++nd;
16     if (L==R){ v[x]=(Tr){0,0,a[L],0}; return; }
17     int mid=(L+R)>>1;
18     build(lson); build(rson);
19     v[x].sm=v[v[x].ls].sm+v[v[x].rs].sm; v[x].tag=0;
20 }
21
22 void ins(int y,int &x,int L,int R,int l,int r,int k){
23     x=++nd; v[x]=v[y]; v[x].sm+=1ll*(r-l+1)*k;
24     if (L==l && r==R){ v[x].tag+=k; return; }
25     int mid=(L+R)>>1;
26     if (r<=mid) ins(v[y].ls,lson,l,r,k);
27     else if (l>mid) ins(v[y].rs,rson,l,r,k);
28         else ins(v[y].ls,lson,l,mid,k),ins(v[y].rs,rson,mid+1,r,k);
29 }
30
31 ll que(int x,int L,int R,int l,int r){
32     if (L==l && r==R) return v[x].sm;
33     int mid=(L+R)>>1,res=v[x].tag*(r-l+1);
34     if (r<=mid) return res+que(lson,l,r);
35     else if (l>mid) return res+que(rson,l,r);
36         else return res+que(lson,l,mid)+que(rson,mid+1,r);
37 }
38
39 int main(){
40     freopen("hdu4348.in","r",stdin);
41     freopen("hdu4348.out","w",stdout);
42     while (~scanf("%d%d",&n,&m)){
43         if (flag) puts(""); else flag=1;
44         rep(i,1,n) scanf("%d",&a[i]);
45         nd=tim=0; build(rt[0],1,n);
46         rep(i,1,m){
47             scanf(" %c",&op);
48             if (op==‘C‘) scanf("%d%d%d",&l,&r,&k),tim++,ins(rt[tim-1],rt[tim],1,n,l,r,k);
49             if (op==‘Q‘) scanf("%d%d",&l,&r),printf("%lld\n",que(rt[tim],1,n,l,r));
50             if (op==‘H‘) scanf("%d%d%d",&l,&r,&k),printf("%lld\n",que(rt[k],1,n,l,r));
51             if (op==‘B‘) scanf("%d",&tim);
52         }
53     }
54     return 0;
55 }

原文地址:https://www.cnblogs.com/HocRiser/p/10421619.html

时间: 2024-10-03 23:52:50

[HDU4348]To the moon(主席树+标记永久化)的相关文章

SP11470 TTM - To the moon[主席树标记永久化]

SP11470 TTM - To the moon C l r d:区间 \([L,R]\) 中的数都加 d ,同时当前的时间戳加 1. Q l r:查询当前时间戳区间 \([L,R]\) 中所有数的和 . H l r t:查询时间戳 \(t\) 区间 \([L,R]\) 的和 . B t:将当前时间戳置为 \(t\) 每次记下版本 \(times\) 保留标记-每次标记的时候直接加上\(tag_p\) 即可 #include <bits/stdc++.h> #define ls(x) ch[

[BZOJ 4826]影魔 区间修改主席树 标记永久化

为了这道题还特地去学了标记永久化,可能对于区间修改主席树或者树套树比较有用吧OvO 我们可以把答案分为两部分:p1造成的和p2造成的 我们枚举序列,用单调栈求出序列每一个位置i,左右边第一个比它大的L,R 开三棵主席树tree1 tree2 tree3 把L扔进tree1的R位置(单点+1),L+1~i-1扔进tree2的R位置,i+1~R-1扔进tree3的L位置(区间+1) 然后询问[l,r]的时候,求出三棵区间主席树 p1造成的贡献为区间tree1内大于等于L的个数 p2造成的贡献为区间t

[知识点]线段树标记永久化

前言: 本文由Hallmeow原创,转载请注明出处! 由于打丧心病狂的 [BZOJ 4826]影魔  导致需要学习标记永久化,于是入坑OvO 知识点:线段树标记永久化 对于树套树,主席树等使用到线段树的比较复杂的数据结构,如果我们区间修改的话,打标记后pushdown或者pushup是很费劲的 那么我们能不能不用pushdown和pushup呢?当然可以啦!这样就用到标记永久化了! 原理就是: 在路过该节点的时候把修改对答案的影响加上,来省去标记下放的过程 实现起来: 线段树的每个节点维护 su

线段树标记永久化

前言 对于树套树,主席树等使用到线段树的比较复杂的数据结构,如果区间修改的话,打标记后pushdown或者pushup是很难做到的完全不行吧 所以这个时候,一个神奇的东西诞生了... 正题 线段树标记永久化,维护一个标记,假设为cov,再维护一个sum 假设修改区间[ql, qr]全部加上v: 和平常一样,到这个区间后cov[x] += v 但是我们又不想pushup,怎么办? 很好做,更新的时候每次sum[x] += v * (qr - ql + 1) (注意这里的qr,ql是完全被包含于线段

POJ 2155 Matrix 二维线段树+标记永久化?

题意:链接 方法:二维线段树+标记永久化 解析:题意是比较明朗的,算一下内存发现4000^2也是可以接受的,于是就开始yy二维线段树? 对于第一层x的线段树,每个节点开一整棵y线段树. 用个二维数组就实现了? 不过发现个问题啊,这题怎么pushdown啊,标记传不下去啊.如果给x打个标记那么怎么知道y传那段啊? 于是就学了新的东西?标记永久化. 本题是单点查询嘛,标记永久化就应该是把相应的区间直接异或,不用往下传?那查询的时候怎么办呢?只需要在查询的时候把所有路过该点的区间都异或起来就OK了.貌

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

【BZOJ1513】【POI2006】Tet-Tetris 3D 二维线段树+标记永久化

题解:题意很裸啊~~~ 培训的时候说要写标记永久化,反正永久化很水,就直接写了. 但是我并不知道为什么要永久化,或者说理解不深刻,但是再遇上肯定能分析出来233. 大概应该可能或许就是: 直接原因:下传标记传不下去. 根本原因: 线段树有两层,这样它的传递可能就有点像拓扑了 就是外层线段树需要往内层线段树传,然后内层线段树还要下传 这样扫到某处时发现,****,还需要顺着两边的标记路径回溯到根, 然后各种压栈啊什么的,才能传下去,而且大概率出错(代码错或思路错). 所以简简单单写个标记永久化好了

HFUUOJ1024 动态开点线段树+标记永久化

题意 分析 动态加点线段树,标记永久化好写常数小 Code #include<bits/stdc++.h> #define fi first #define se second #define lson l,mid,p<<1 #define rson mid+1,r,p<<1|1 #define pb push_back #define ll long long using namespace std; const ll inf=1e18; const int mod=