题目大意:
题目大意:给定一些数字,有以下几个操作
a、[L,R]区间的数字变化D的数值。并且时间向前推进
b、询问某个时间的[L,R]区间的数字之和,这不花费时间
c、回到X时间。
可持久话线段树裸题。
其实区间修改就体现了,只要主席树更新的节点,就要重建。
就本题而言,区间加我可以搞成标记永久化,防止pushdown新建太多的节点,感觉尽量不要通过pushdown加太多节点,想办法搞成标记永久化
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<stack>
using namespace std;
const int N=100005;
typedef long long ll;
struct aa
{
int lc,rc,l,r;
ll add,sum;
int len(){return r-l+1;}
}a[N*4*200];
int n,m,rt[N],time,tot;
void build(int &u,int l,int r)
{
u=++tot;
a[u].l=l,a[u].r=r;
if (l==r) {scanf("%lld",&a[u].sum);return ;}
int mid=(l+r)>>1;
build(a[u].lc,l,mid);
build(a[u].rc,mid+1,r);
a[u].sum=a[a[u].lc].sum+a[a[u].rc].sum;
}
void insert(int &now,int old,int l,int r,ll d)
{
now=++tot;
a[now]=a[old];
if (a[old].l==l&&a[old].r==r)
{
a[now].add+=d;
return;
}
a[now].sum+=d*(r-l+1);
int mid=(a[now].l+a[now].r)>>1;
if (r<=mid) insert(a[now].lc,a[old].lc,l,r,d);
else if (mid<l) insert(a[now].rc,a[old].rc,l,r,d);
else
{
insert(a[now].lc,a[old].lc,l,mid,d);
insert(a[now].rc,a[old].rc,mid+1,r,d);
}
}
ll query(int u,int l,int r)
{
ll tmp=a[u].add*(r-l+1);
if (a[u].l==l&&a[u].r==r) return a[u].sum+tmp;
int mid=(a[u].l+a[u].r)>>1;
if (r<=mid) return tmp+query(a[u].lc,l,r);
else if (mid<l) return tmp+query(a[u].rc,l,r);
return tmp+query(a[u].lc,l,mid)+query(a[u].rc,mid+1,r);
}
int back[N];
int main()
{
scanf("%d%d",&n,&m);
build(rt[0],1,n);
time=0;
char ch[2];
int x,y,t;ll d;
for (int i=1;i<=m;i++)
{
scanf("%s",ch);
switch(ch[0])
{
case 'C':scanf("%d%d%lld",&x,&y,&d);time++;insert(rt[time],rt[time-1],x,y,d);back[time]=tot;break;
case 'Q':scanf("%d%d",&x,&y);printf("%lld\n",query(rt[time],x,y));break;
case 'H':scanf("%d%d%d",&x,&y,&t);printf("%lld\n",query(rt[t],x,y));break;
case 'B':scanf("%d",&time);tot=back[time];break;
}
}
return 0;
}