题意:给你下标从1开始到n的n个数字。有一个时间戳,一开始是0。
有四种操作:
1.C l r d 区间[l,r]中的每个数字都加上d,并且时间戳+1
2.Q l r 询问区间[l,r]的区间和
3.H l r t 询问在时间戳为t的时候[l,r]的区间和
4.B t 把时间戳变为t。
这题可以用主席树来做,时间戳可以用主席树的版本来处理。用root[t]来表示t时刻的信息,然后在root[t]中询问区间和就可以了。由于有区间更新,所以主席树永久标志还是少不了的,否则肯定要超时了。从第4个操作可以发现,时间戳变为t之后,其实时间比t晚的信息都是没有用的,这样的话可以直接把tot更新一下,可以大大节约空间。别的都和普通题目没啥差别。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=100005;
ll root[maxn],lson[maxn*20],rson[maxn*20],tsize[maxn*20],lazy[maxn*20];
ll num[maxn];
ll tot,cnt;
void build(ll rt,ll l,ll r)
{
if(l==r)
{
tsize[rt]=num[++cnt];
return;
}
ll mid=(l+r)>>1;
build(lson[rt]=++tot,l,mid);
build(rson[rt]=++tot,mid+1,r);
tsize[rt]=tsize[lson[rt]]+tsize[rson[rt]];//向上更新
}
void updata(ll last,ll cur,ll l,ll r,ll x,ll y,ll k)
{
lson[cur]=lson[last];
rson[cur]=rson[last];
lazy[cur]=lazy[last];
tsize[cur]=tsize[last];
tsize[cur]+=k*(y-x+1);
if(l==x&&r==y)
{
lazy[cur]+=k;
return;
}
ll mid=(l+r)>>1;
if(y<=mid)updata(lson[last],lson[cur]=++tot,l,mid,x,y,k);
else if(x>mid)updata(rson[last],rson[cur]=++tot,mid+1,r,x,y,k);
else updata(lson[last],lson[cur]=++tot,l,mid,x,mid,k),updata(rson[last],rson[cur]=++tot,mid+1,r,mid+1,y,k);
}
ll query(ll rt,ll l,ll r,ll x,ll y)
{
if(l==x&&r==y)return tsize[rt];
ll mid=(l+r)>>1;
ll tp=lazy[rt]*(y-x+1);//查询的时候不要忘了还要加上延时标记
if(y<=mid)return query(lson[rt],l,mid,x,y)+tp;
else if(x>mid)return query(rson[rt],mid+1,r,x,y)+tp;
else return query(lson[rt],l,mid,x,mid)+query(rson[rt],mid+1,r,mid+1,y)+tp;
}
int main()
{
ll n,m;
while(~scanf("%lld%lld",&n,&m))
{
for(ll i=1;i<=n;i++)
scanf("%lld",&num[i]);
memset(lazy,0,sizeof(lazy));
memset(tsize,0,sizeof(tsize));
tot=cnt=0;
build(root[0]=++tot,1,n);
char o[2];
ll x,y,z;
ll now=0;
for(ll i=1;i<=m;i++)
{
scanf("%s",o);
if(o[0]=='Q')
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",query(root[now],1,n,x,y));
}
else if(o[0]=='C')
{
scanf("%lld%lld%lld",&x,&y,&z);
now++;
updata(root[now-1],root[now]=++tot,1,n,x,y,z);
}
else if(o[0]=='H')
{
scanf("%lld%lld%lld",&x,&y,&z);
printf("%lld\n",query(root[z],1,n,x,y));
}
else if(o[0]=='B')
{
scanf("%lld",&x);
if(x<now)tot=root[x+1];//把tot变为x时间戳之后一个的tot
now=x; //题目好像保证新的时间戳比当前的要早,其实不需要if
}
}
}
}