树状数组之区间更新与查询
具体思路:由于树状数组裸的模板只能通过数组求区间和,而对于区间的更新的查询无法实现,所以通过多个数组进行辅助。
具体公式,通过三个数组实现。第一个数组记录第一组的前缀和。然后如果是更新的话,举个例子,一共有10个数,1~n.在5 8 之间每一个数加3,也就是总的和在原来的基础上上加上了12,我们可以这样记录,从第5个到最后一个都加上3,然后从的第九个到最后一个都减去3.这样就记录好了。具体实现这个操作的可以通过一个数组arr实现。arr[i]记录的是从第i个数到最后一个数变化了多少。
sum(l,r)=a[r]-a[l-1]+arr[i]*(r+1)+i*arr[i] (l < = i < = r),然后再开两个数组记录arr[i]和i*arr[i]的前缀和就可以了。然后计算的时候按照前缀和的做法就行了,然后按照公式来就行了。
题目链接:
https://vjudge.net/contest/66989#problem/C
AC代码(有时间具体解释):
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stdio.h>
using namespace std;
# define inf 0x3f3f3f3f
# define maxn 100000+100
# define ll long long
ll ans[maxn];
ll ansi[maxn];
ll s[maxn];
ll n,m;
ll lowbit(ll t)
{
return t&(-t);
}
void update(ll t1,ll t2,ll *t)
{
while(t1<=n)
{
t[t1]+=t2;
t1+=lowbit(t1);
}
}
ll sum(ll t1,ll *t)
{
ll ans=0;
while(t1>=1)
{
ans+=t[t1];
t1-=lowbit(t1);
}
return ans;
}
int main()
{
scanf("%lld%lld",&n,&m);
memset(s,0,sizeof(s));
memset(ans,0,sizeof(ans));
memset(ansi,0,sizeof(ansi));
for(int i=1; i<=n; i++)
{
scanf("%lld",&s[i]);
s[i]+=s[i-1];
}
char str[10];
ll t1,t2,t3;
while(m--)
{
scanf("%s",str);
if(str[0]=='Q')
{
scanf("%lld%lld",&t1,&t2);
printf("%lld\n",s[t2]-s[t1-1]+(t2+1)*sum(t2,ans)-t1*sum(t1-1,ans)-(sum(t2,ansi)-sum(t1-1,ansi)));
}
else if(str[0]=='C')
{
scanf("%lld%lld%lld",&t1,&t2,&t3);
update(t1,t3,ans);
update(t2+1,-t3,ans);
update(t1,t1*t3,ansi);
update(t2+1,-(t2+1)*t3,ansi);
}
}
return 0;
}