题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4348
题目大意:一开始给你一个序列,并令时间戳为1。现在有4种操作:C l r d:将当前序列的[l,r]全部+d,并令时间戳+1;Q l r:查询当前序列[l,r]的和;H l r t查询t时刻序列[l,r]的和;B t:将时间戳设为t。
分析:考虑到维护历史版本以及区间操作,我们考虑带懒惰标记的可持久化线段树。我们想一下,一次区间操作,最多需要访问多少个节点?本人认为是接近4*log(n)个:
如果我们在每一次操作的时候下放懒惰标记的话,空间就承受不住了。于是我们考虑不要下放标记,但这样就和普通的线段树有些不同,我们在统计答案的时候,要考虑一路上经过的节点的懒惰标记对答案的影响:
如上图,我们再查询[6,8]的和的时候,由于没有下放标记,[5,8]的节点处还有+2标记,它也会影响答案,于是我们在递归每一个节点[l,r]时求出该区间与询问区间[L,R]的交集长度,再乘以懒惰标记,加进答案里。比如上图,我们在递归[5,8]时,发现它与[6,8]的交集长度为3,就将答案+3*2。
这样我们的理论空间复杂度就降到了m*4*log(n),然而还是会炸。于是我们将空间开到稍微比题目空间限制小一点就可以A了(数据也没这么刁钻)。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=100100;
const int lg=13;
struct Tnode
{
LL add,sum;
Tnode *lson,*rson;
} tree[maxn*lg*2];
Tnode *Root[maxn];
int cur;
int a[maxn];
LL sum[maxn];
int n,m;
int t;
Tnode *New_node()
{
tree[ ++cur ].add=0;
tree[cur].sum=0;
tree[cur].lson=tree[cur].rson=tree;
return tree+cur;
}
LL Query(Tnode *root,int L,int R,int x,int y)
{
if ( y<L || R<x ) return 0;
if ( x<=L && R<=y ) return root->sum;
int mid=(L+R)>>1;
LL vl=Query(root->lson,L,mid,x,y);
LL vr=Query(root->rson,mid+1,R,x,y);
int low=max(L,x);
int high=min(R,y);
return (long long)(high-low+1)*root->add+vl+vr;
}
bool In(int L,int R,int x,int y)
{
return !( y<L || R<x );
}
void Update(Tnode *root,int L,int R,int x,int y,LL v)
{
if ( x<=L && R<=y )
{
root->add+=v;
root->sum+=( (long long)(R-L+1)*v );
return;
}
int mid=(L+R)>>1;
if ( In(L,mid,x,y) )
{
Tnode *z=New_node();
*z=*(root->lson);
root->lson=z;
Update(z,L,mid,x,y,v);
}
if ( In(mid+1,R,x,y) )
{
Tnode *z=New_node();
*z=*(root->rson);
root->rson=z;
Update(z,mid+1,R,x,y,v);
}
root->sum=root->lson->sum+root->rson->sum+root->add*(long long)(R-L+1);
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d",&n);
while ( 1 )
{
scanf("%d",&m);
for (int i=1; i<=n; i++) scanf("%d",&a[i]);
sum[0]=0;
for (int i=1; i<=n; i++) sum[i]=sum[i-1]+(long long)a[i];
cur=-1,t=0;
Root[0]=New_node();
for (int i=1; i<=m; i++)
{
char c=getchar();
while ( c!='H' && c!='B' && c!='C' && c!='Q' ) c=getchar();
if (c=='B') scanf("%d",&t);
if (c=='H')
{
int L,R,lt;
scanf("%d%d%d",&L,&R,<);
long long ans=Query(Root[lt],1,n,L,R);
ans+=sum[R];
ans-=sum[L-1];
cout<<ans<<endl;
}
if (c=='Q')
{
int L,R;
scanf("%d%d",&L,&R);
long long ans=Query(Root[t],1,n,L,R);
ans+=sum[R];
ans-=sum[L-1];
cout<<ans<<endl;
}
if (c=='C')
{
int L,R,x;
scanf("%d%d%d",&L,&R,&x);
Root[ ++t ]=New_node();
*Root[t]=*Root[t-1];
Update(Root[t],1,n,L,R,x);
}
}
n=0;
scanf("%d",&n);
if (!n) break;
printf("\n");
}
//printf("%d\n",sizeof(tree)/1024);
return 0;
}