這裏是樹狀數組頻道。
哈!經過我深度挖掘各種blogs,終於看懂了!太辣雞了我!
題目大意
給你N個數,有兩種操作:
1:給區間[a,b]的所有數增加X
2:詢問區間[a,b]的數的和。
數據範圍:1<=n<=200000,1<=q<=200000
題目分析
一直覺得樹狀數組是個很神奇的東西。
如果你已經看過很多篇blogs覺得走投無路了,壓抑住(huaji)還是請認真看看ba!
雖然我非常非常蒟,但是我語文(我也需要表達對吧)數學還是過得去的(屁)...
如果你不會樹狀數組的改點求段和改段求點的話,對不起我幫不了你(踢下車/誠懇/)。
首先介紹差分數組c[],emm是個好東西
c[1]=a[1]−a[0]
c[2]=a[2]−a[1]
c[3]=a[3]−a[2]
...
c[n]=a[n]−a[n−1]
改段求點的核心也就是這個啦。
那麼接着來!!!(我加粗了!重要!)
a[1]+a[2]+...+a[n]
=(c[1])+(c[1]+c[2])+...+(c[1]+c[2]+...+c[n])
= n*c[1]+(n−1)*c[2]+...+c[n]
= n*(c[1]+c[2]+...+c[n])−(0*c[1]+1*c[2]+...+(n-1)*c[n])
那麼我們就維護一個數組cc[n],其中cc[i]=(i−1)*c[i]。
上面的黑塊摘自:https://blog.csdn.net/weixin_41190227/article/details/82666855
一起來舉一反三ba
求區間[L,R]的值:
a[L]+a[L+1]+....+a[R]
=(a[1]+a[2]+...+a[R])−(a[1]+a[2]+...+a[L−1])
=((c[1])+(c[1]+c[2])+...+(c[1]+c[2]+...+c[R]))
−((c[1])+(c[1]+c[2])+...+(c[1]+c[2]+...+c[L−1]))
=(R*c[1]+(R−1)*c[2]+...+1*c[R])−((L−2)*c[1]+(L−3)*c[2]+...+1*c[L−1])
=(R*(c[1]+c[2]+...+c[R])−(0*c[1]+1*c[2]+...+(R−1)*c[R]))
−((L−1)*(c[1]+c[2]+...+c[L−1])−(0*c[1]+1*c[2]+...+(L−2)*c[L−1]))
=(R*sum(c,R)−sum(cc,R))−((L−1)*sum(c,L−1)−sum(c1,L−1))
你一定看懂了昂(大霧),恭喜你學會求區間。
然後改段跟改段求點裏面的是一模一樣的...
你修改c的時候,順手修改一下cc就好了。
add(c,x,k);
add(c,y+1,-k);//修改c
add(cc,x,(x-1)*k);
add(cc,y+1,y*(-k));//修改cc
以上。
代碼
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m; long long x,xx=0;
long long c[200010],cc[200010];
int lowbit(int x) { return x&-x; }
void add(long long a[],int x,long long k)
{
while(x<=n)
{
a[x]+=k;
x+=lowbit(x);
}
}
long long sum(long long a[],int x)
{
long long s=0;
while(x)
{
s+=a[x];
x-=lowbit(x);
}
return s;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&x);
add(c,i,x-xx);
add(cc,i,(i-1)*(x-xx));
xx=x;
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int t,x,y; long long k;
scanf("%d%d%d",&t,&x,&y);
if(t==1)
{
scanf("%lld",&k);
add(c,x,k); add(c,y+1,-k);
add(cc,x,(x-1)*k); add(cc,y+1,y*(-k));
}
else
{
long long aa=y*sum(c,y)-sum(cc,y);
long long bb=(x-1)*sum(c,x-1)-sum(cc,x-1);
printf("%lld\n",aa-bb);
}
}
return 0;
}