传说可以用树状数组,不过因为本人比较菜,于是上无脑差分数组+线段树。
容易发现,当一个数i增加x时,会使前缀和的前缀和j(j>=i)增加(j-i+1)*x。那么可以想象成i~n段加x,i+1~n段加x,i+2~n段加x……。
那么用差分数组就可以直接改i~n,然后可以求前缀和求答案。(前缀和的前缀和的差分前缀和?有点绕)
具体还是代码吧。语文死的早。
code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
struct node{
int l,r,lc,rc;
LL c,u;
}tr[200010];int trlen=0;
int n,m,a[100010];
LL sum[100010];
int bt(int l,int r)
{
int x=++trlen;
tr[x].l=l;tr[x].r=r;tr[x].c=0;tr[x].u=0;
if(l!=r)
{
int mid=(l+r)/2;
tr[x].lc=bt(l,mid);
tr[x].rc=bt(mid+1,r);
}
return x;
}
void update(int x)
{
int lc=tr[x].lc,rc=tr[x].rc;LL u=tr[x].u;
tr[lc].c+=u*(LL)(tr[lc].r-tr[lc].l+1);
tr[rc].c+=u*(LL)(tr[rc].r-tr[rc].l+1);
tr[lc].u+=u;tr[rc].u+=u;
tr[x].u=0;
}
void change(int x,int l,int r,LL c)
{
if(tr[x].l==l&&tr[x].r==r)
{
tr[x].u+=c;
tr[x].c+=c*(r-l+1);return;
}
int mid=(tr[x].l+tr[x].r)/2,lc=tr[x].lc,rc=tr[x].rc;
if(tr[x].u!=0) update(x);
if(r<=mid) change(lc,l,r,c);
else if(l>mid) change(rc,l,r,c);
else change(lc,l,mid,c),change(rc,mid+1,r,c);
tr[x].c=tr[lc].c+tr[rc].c;
}
LL findans(int x,int l,int r)
{
if(tr[x].l==l&&tr[x].r==r) return tr[x].c;
int mid=(tr[x].l+tr[x].r)/2,lc=tr[x].lc,rc=tr[x].rc;
if(tr[x].u!=0) update(x);
if(r<=mid) return findans(lc,l,r);
if(l>mid) return findans(rc,l,r);
return findans(lc,l,mid)+findans(rc,mid+1,r);
}
int main()
{
scanf("%d %d",&n,&m);a[0]=0;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=(LL)a[i];
for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
bt(1,n);change(1,1,1,sum[1]);
for(int i=2;i<=n;i++) change(1,i,i,sum[i]-sum[i-1]);
char s[5];
while(m--)
{
scanf("%s",s+1);int x,y;
scanf("%d",&x);
if(s[1]=='M') scanf("%d",&y),change(1,x,n,(LL)(y-a[x])),a[x]=y;
else printf("%lld\n",findans(1,1,x));
}
}