树状数组之区间更新与查询

具体思路:由于树状数组裸的模板只能通过数组求区间和,而对于区间的更新的查询无法实现,所以通过多个数组进行辅助。

具体公式,通过三个数组实现。第一个数组记录第一组的前缀和。然后如果是更新的话,举个例子,一共有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;
}

 

posted @ 2018-10-11 18:30  Let_Life_Stop  阅读(276)  评论(0编辑  收藏  举报