线段树历史版本和
假设当前进行到操作 $m$.
1. 将区间 $[l,r]$ 每个数加上 $v$.
2. 询问当前区间 $[l,r]$ 的和.
3. 令 $S(l,r,x)$ 代表 $[l,r]$ 区间在时刻 $x$ 时之和,求 $\sum_{i=0}^{m} S(l,r,i)$.
题解:
对于一个区间,我们要求当前区间和以及所有历史时刻之和.
考虑维护 $sum, sumh$ 分别表示当前和以及历史和.
如果没有加法标记,我们可以直接存一个 $tag$,表示 $sumh \leftarrow sumh+tag \times sum.$
然后我们发现存在加法标记的情况下要先下传加法标记,再下传 $tag.$
考虑下传加法标记:
$sum \leftarrow sum+len \times v$
$add \leftarrow add+v$
$sumh \leftarrow sumh+tag \times sum$.
对于点 $x$ 来说,假设原来有 $v1,tag1$,那么现在变为 $v1+v$,$tag1$.
而根据定义,$tag1$ 应该只和 $v1$ 结合,所有加多了一部分,那么就再设标记 $addh$ 表示 $sumh$ 需要减去的值就好了.
下传标记的顺序是:$add$,$addh$,$tag$.
code:
#include <cstdio> #include <cstring> #include <algorithm> #define N 100008 #define ll long long #define lson now<<1 #define rson now<<1|1 #define setIO(s) freopen(s".in","r",stdin) using namespace std; int a[N],n,m; struct data { int len; ll sum,add,sumh,tag,addh; }s[N]; void pushup(int now) { s[now].sum=s[lson].sum+s[rson].sum; s[now].sumh=s[lson].sumh+s[rson].sumh; } void mark_adh(int now,ll v,int t) { s[now].addh+=v; if(t) s[now].sumh+=v*s[now].len; } void mark_add(int now,ll v) { if(s[now].tag) { mark_adh(now,-v*s[now].tag,0); } s[now].add+=v; s[now].sum+=1ll*s[now].len*v; } void mark_tag(int now,ll v) { s[now].tag+=v; s[now].sumh+=s[now].sum*v; } void pushdown(int now) { if(s[now].add) { mark_add(lson,s[now].add); mark_add(rson,s[now].add); s[now].add=0; } if(s[now].addh) { mark_adh(lson,s[now].addh,1); mark_adh(rson,s[now].addh,1); s[now].addh=0; } if(s[now].tag) { mark_tag(lson,s[now].tag); mark_tag(rson,s[now].tag); s[now].tag=0; } } void build(int l,int r,int now) { s[now].len=r-l+1; if(l==r) { s[now].sum=a[l]; s[now].sumh=a[l]; return; } int mid=(l+r)>>1; build(l,mid,lson); build(mid+1,r,rson); pushup(now); } void update(int l,int r,int now,int L,int R,int v) { if(l>=L&&r<=R) { mark_add(now,v); return; } pushdown(now); int mid=(l+r)>>1; if(L<=mid) update(l,mid,lson,L,R,v); if(R>mid) update(mid+1,r,rson,L,R,v); pushup(now); } ll query(int l,int r,int now,int L,int R) { if(l>=L&&r<=R) { return s[now].sumh; } pushdown(now); int mid=(l+r)>>1; ll re=0; if(L<=mid) re+=query(l,mid,lson,L,R); if(R>mid) re+=query(mid+1,r,rson,L,R); return re; } int main() { // setIO("input"); // freopen("de.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) { scanf("%d",&a[i]); } build(1,n,1); int x,y,z,l,r; for(int i=1;i<=m;++i) { scanf("%d%d%d",&l,&r,&x); update(1,n,1,l,r,1ll*x); mark_tag(1,1); scanf("%d%d",&x,&y); printf("%lld\n",query(1,n,1,x,y)); } return 0; }
暴力:
#include <bits/stdc++.h> #define N 1002 #define ll long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; ll a[N][N],sum[N][N]; int main() { setIO("input"); freopen("input.out","w",stdout); int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%lld",&a[0][i]),sum[0][i]=a[0][i]; for(int i=1;i<=m;++i) { int l,r,x,L,R; scanf("%d%d%d",&l,&r,&x); for(int j=1;j<=n;++j) a[i][j]=a[i-1][j]; for(int j=l;j<=r;++j) a[i][j]+=1ll*x; for(int j=1;j<=n;++j) sum[i][j]=sum[i-1][j]+a[i][j]; scanf("%d%d",&L,&R); ll s=0; for(int j=L;j<=R;++j) s+=sum[i][j]; printf("%lld\n",s); } return 0; } /* input: 5 3 1 2 3 4 5 1 1 1 2 4 1 3 2 2 4 1 3 3 1 5 output: 18 31 84 */
数据生成器及对拍:
#include <bits/stdc++.h> using namespace std; int RAN(int p) { return rand()%p+1; } void make() { FILE *fp=fopen("input.in","w"); int n=1000,m=1000; fprintf(fp,"%d %d\n",n,m); for(int i=1;i<=n;++i) { fprintf(fp,"%d ",RAN(1000)); } fprintf(fp,"\n"); for(int i=1;i<=m;++i) { int l=RAN(n),r=RAN(n); if(l>r) swap(l,r); fprintf(fp,"%d %d %d ",l,r,RAN(1000)); int L=RAN(n),R=RAN(n); if(L>R) swap(L,R); fprintf(fp,"%d %d\n",L,R); } fclose(fp); } void gen() { make(); } int main() { system("g++ std.cpp -o std.exe -g -O2 -std=c++11 -Wl,-stack=512000000"); system("g++ code.cpp -o code.exe -g -O2 -std=c++11 -Wl,-stack=512000000"); srand(time(NULL)); rand(); int times=0; while(1) { gen(); system("std.exe < input.in > de.out"); int s=clock(); system("code.exe < input.in > input.out"); int t=clock(); if(system("fc input.out de.out > FC.out")) { printf("WA\n"); break; } else { printf("%d : AC : %d\n",++times,t-s); } } return 0; }