http://acm.hdu.edu.cn/showproblem.php?pid=4348
题意:给出一个n,m分别表示数组的长度和操作的次数。
C l r x:表示在区间[l,r]每一个数加上x,同时时间戳加1;
Q l r:表示询问当前时间戳区间[l,r]的区间和;
H l r x:表示询问在时间戳为x时,区间[l,r]的区间和;
B t:把时间戳设置为t;
做法:考虑可持久化主席树,同时标记一个lazy在更新和查询时注意加上去就可以了。
在建树时最好就把数据存入。关于时间戳的变换这个直接变换root就可以了。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int root[N],cnt,n,m;
int ls[N*40],rs[N*40],add[N*40];
long long sum[N*40];
int build(int l,int r)
{
int now=++cnt;
add[now]=0;
if(l==r){
scanf("%lld",&sum[now]);
return now;
}
int mid=(l+r)>>1;
ls[now]=build(l,mid);
rs[now]=build(mid+1,r);
sum[now]=sum[ls[now]]+sum[rs[now]];
return now;
}
int update(int old,int l,int r,int ql,int qr,int x)
{
int now=++cnt;
ls[now]=ls[old],rs[now]=rs[old],add[now]=add[old];
sum[now]=sum[old]+1LL*x*(min(qr,r)-max(ql,l)+1);
if(ql<=l&&r<=qr){
add[now]+=x;
return now;
}
int mid=(l+r)>>1;
if(ql<=mid) ls[now]=update(ls[old],l,mid,ql,qr,x);
if(qr>mid) rs[now]=update(rs[old],mid+1,r,ql,qr,x);
return now;
}
long long query(int old,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr){
return sum[old];
}
long long ans=add[old]*(min(qr,r)-max(ql,l)+1);
int mid=(l+r)>>1;
if(ql<=mid) ans+=query(ls[old],l,mid,ql,qr);
if(qr>mid) ans+=query(rs[old],mid+1,r,ql,qr);
return ans;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
cnt=0;
root[0]=build(1,n);
int now=0,l,r,x;
char op[5];
for(int i=1;i<=m;i++)
{
scanf("%s",op);
if(op[0]=='C'){
scanf("%d%d%d",&l,&r,&x);
root[now+1]=update(root[now],1,n,l,r,x);
now++;
}
else if(op[0]=='Q'){
scanf("%d%d",&l,&r);
printf("%lld\n",query(root[now],1,n,l,r));
}
else if(op[0]=='H'){
scanf("%d%d%d",&l,&r,&x);
printf("%lld\n",query(root[x],1,n,l,r));
}
else{
scanf("%d",&x);now=x;
}
}
}
return 0;
}