当前位置: 代码迷 >> 综合 >> hdu4348 To the moon(可持久化线段树+标记永久化)
  详细解决方案

hdu4348 To the moon(可持久化线段树+标记永久化)

热度:28   发布时间:2024-02-04 21:48:23.0

题意:

给定长度为n的序列,m次操作,一开始的时间戳为0
操作有四种:
(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 。即回到过去t时刻。

数据范围:n,m<=1e5

解法:

显然可持久化,
但是涉及区间修改操作,所以要将标记永久化,而不能pushdown,因为pushdown需要新建的区间点太多。

标记永久化就是给节点打上一个区间修改标记tag,但是不下传,

当查询的区间[st,ed]完全被当前区间[l,r]包含时,tag可以用于统计答案
和平常线段树不太一样的地方:
1.因为[st,ed]需要完全被[l,r]包含,所以线段树的写法和平常不太一样,是拆区间的写法,
线段树常见写法:

if(st<=mid)update(st,ed,val,l,mid,node*2);
if(ed>mid)update(st,ed,val,mid+1,r,node*2+1);

标记永久化必须用的写法:

if(ed<=mid)update(st,ed,val,l,mid,node*2);
else if(st>mid)update(st,ed,val,mid+1,r,node*2+1);
else update(st,mid,val,l,mid,node*2),update(mid+1,ed,val,mid+1,r,node*2+1);

因为要时刻保证[st,ed]被[l,r]完全包含

2.因为每个区间节点都有一个tag,不同区间节点之间的tag是相互独立、没有联系的,
所以pushup的写法也不一样,
例如现在要维护区间和,操作为区间加法,那么pushup是这样的:

void pushup(int k,int l,int r){//k是节点编号sum[k]=sum[lc[k]]+sum[rc[k]]+add[k]*(r-l+1);
}

每次的add标记都要计算进去,因为和普通laz标记不一样,没有被下传然后清空

-----分割线-----

暂时就这么多发现

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
int lc[maxm*40],rc[maxm*40],sum[maxm*40],add[maxm*40];
int rt[maxm],tot;
int now;
//
int a[maxm];
int n,m;
void init(){tot=0;now=0;
}
void pushup(int k,int l,int r){sum[k]=sum[lc[k]]+sum[rc[k]]+add[k]*(r-l+1);
}
void build(int l,int r,int &k){k=++tot;lc[k]=rc[k]=add[k]=0;if(l==r){sum[k]=a[l];return ;}int mid=(l+r)/2;build(l,mid,lc[k]);build(mid+1,r,rc[k]);pushup(k,l,r);
}
void update(int st,int ed,int val,int l,int r,int last,int &k){k=++tot;//lc[k]=lc[last],rc[k]=rc[last];sum[k]=sum[last],add[k]=add[last];//if(st==l&&ed==r){sum[k]+=val*(ed-st+1);add[k]+=val;return ;}int mid=(l+r)/2;if(ed<=mid)update(st,ed,val,l,mid,lc[last],lc[k]);else if(st>mid)update(st,ed,val,mid+1,r,rc[last],rc[k]);else update(st,mid,val,l,mid,lc[last],lc[k]),update(mid+1,ed,val,mid+1,r,rc[last],rc[k]);pushup(k,l,r);
}
int ask(int st,int ed,int l,int r,int k){if(!k)return 0;if(st==l&&ed==r)return sum[k];int mid=(l+r)/2;int ans=add[k]*(ed-st+1);//这时候[st,ed]一定被[l,r]全包含if(ed<=mid){ans+=ask(st,ed,l,mid,lc[k]);}else if(st>mid){ans+=ask(st,ed,mid+1,r,rc[k]);}else{ans+=ask(st,mid,l,mid,lc[k])+ask(mid+1,ed,mid+1,r,rc[k]);}return ans;
}
signed main(){ios::sync_with_stdio(0);while(cin>>n>>m){init();for(int i=1;i<=n;i++)cin>>a[i];build(1,n,rt[now]);while(m--){char op;cin>>op;if(op=='C'){int l,r,d;cin>>l>>r>>d;now++;update(l,r,d,1,n,rt[now-1],rt[now]);}else if(op=='Q'){int l,r;cin>>l>>r;int ans=ask(l,r,1,n,rt[now]);cout<<ans<<endl;}else if(op=='H'){int l,r,t;cin>>l>>r>>t;int ans=ask(l,r,1,n,rt[t]);cout<<ans<<endl;}else if(op=='B'){int x;cin>>x;tot=rt[x+1]-1;now=x;}}}return 0;
}