树状数组分为三大类:
1.单点修改+区间求和
2.区间修改+单点求和(运用工具差分数组)
3.区间修改+区间求和(维护两个数组的前缀和)
典型的差分思想:
https://www.luogu.com.cn/problem/P3655
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
ll N,S,Q,T,c[maxn],ls;
ll ans;
inline ll cc(ll x)
{
if(x>0)
return -S*x;
return -T*x;
}
int main()
{
scanf("%lld%lld%lld%lld",&N,&Q,&S,&T);
for(register ll i=0;i<=N;i++)
{
ll x;scanf("%lld",&x);
c[i]=x-ls;
ls=x;
ans+=cc(c[i]);
}
for(register ll i=1;i<=Q;i++)
{
ll x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
ans-=cc(c[x]);
c[x]+=z;
ans+=cc(c[x]);
if(y!=N)
{
ans-=cc(c[y+1]);
c[y+1]-=z;
ans+=cc(c[y+1]);
}
printf("%lld\n",ans);
}
return 0;
}
一.单点修改+区间求和
https://www.luogu.com.cn/problem/P3374
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5+5;
ll c[maxn],d[maxn],n,m;
void add(ll i,ll val)
{
while(i<=n)
{
c[i]+=val;
i+=i&(-i);
}
}
ll ask(ll i)
{
ll ret=0;
while(i>0)
{
ret+=c[i];
i-=i&(-i);
}
return ret;
}
int main()
{
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++)
{
int h;
scanf("%lld",&h);
add(i,h);
}
for(ll i=1;i<=m;i++)
{
ll x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
if(x==1)
{
add(y,z);
}
else
{
cout<<ask(z)-ask(y-1)<<endl;
}
}
return 0;
}
二.区间修改+单点求和(运用工具差分数组)
https://www.luogu.com.cn/problem/P3368
#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+5;
int n,m,a[maxn],c[maxn],d[maxn];
void add(int p,int x)
{
while(p<=n)
{
c[p]+=x,p+=p&-p;
}
}
void range_add(int l,int r,int x)
{
add(l,x),add(r+1,-x);
}
int ask(int p)
{
int res=0;
while(p>0)
res+=c[p],p-=p&-p;
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
add(i,a[i]-a[i-1]);
}
for(int i=1;i<=m;i++)
{
int x,y,z,p;scanf("%d",&x);
if(x==1)
{
scanf("%d%d%d",&y,&z,&p);
range_add(y,z,p);
}
else if(x==2)
{
scanf("%d",&y);
printf("%d\n",ask(y));
}
}
return 0;
}