因为树状数组用得比较少,所以今天总结一下常见用法。(顺便贴一些板)
首先上树状数组的定义:设原数组为a,树状数组为c,那么c[i]=a[i-2^k+1]+a[i-2^k+2]……+a[i]。其中k为i的二进制状态下最后面连续的0的个数。
然后我们就可以得出树状数组的修改和查询操作了。
下面贴一下查询和修改的板:
int find(int x)
{
int i=x,s=0;
while(i>=1){s+=c[i];i=i-(i&(-i));}
return s;
}
int change(int x,int y)
{
int i=x;
while(i<=n){c[i]+=y;i=i+(i&(-i));}
}
树状数组的基本操作就在上面了,下面讲一下一些常见的问题。
1、单点修改+区间查询
这个上面已经讲过了
2、区间修改+单点查询
这一个我们要用到差分思想:设b[i]=a[i]-b[i-1],而c维护的是b的和而不是a的和,那么区间修改x~y的时候我们就只需要修改x和y+1即可。
代码如下:
#include
#include
#include
#define MAXN 500010
int a[MAXN],c[MAXN],n,m,x,y,k,type;
int find(int x)
{
int i=x,s=0;
while(i>=1){s=s+c[i];i=i-(i&(-i));}
return s;
}
int change(int x,int y)
{
int i=x;
if(x==0)return 0;
while(i<=n){c[i]+=y;i=i+(i&(-i));}
}
int main()
{
int i,j;
scanf("%d %d",&n,&m);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
for(i=1;i<=n;i++)change(i,a[i]-a[i-1]);
while(m>=1)
{
scanf("%d",&type);
if(type==1)
{
scanf("%d %d %d",&x,&y,&k);
change(x,k);change(y+1,-k);
}
else
{
scanf("%d",&x);
printf("%d\n",find(x));
}
m--;
}
}
3、区间修改+区间查询
这一个我们还是要用到差分思想,有上面的问题我们的到a[x]=b[1]+b[2]...+b[x],那么我们就有
a[1]+a[2]+a[3]...+a[x]
=b[1]+(b[1]+b[2])+(b[1]+b[2]+b[3])+...+(b[1]+b[2]...+b[x])
=b[1]*x+b[2]*(x-1)+b[3]*(x-2)+...+b[x]*1
=x*(b[1]+b[2]+...+b[x])-(b[1]*0+b[2]*1+b[3]*2+...+b[x]*(x-1))
到这里就十分清晰了。我们只需用两个树状数组,分别维护b[i]的和以及b[i]*(i-1)就可以了。
下面贴一下代码:
#include
#include
#include
#define ll long long
#define MAXN 100010
ll a[MAXN],c1[MAXN],c2[MAXN],n,m,x,y,k;
char type;
ll find1(ll x)
{
ll i=x,s=0;
while(i>=1){s+=c1[i];i=i-(i&(-i));}
return s;
}
ll find2(ll x)
{
ll i=x,s=0;
while(i>=1){s+=c2[i];i=i-(i&(-i));}
return s;
}
int change1(ll x,ll y)
{
ll i=x;
while(i<=n){c1[i]+=y;i=i+(i&(-i));}
return 0;
}
int change2(ll x,ll y)
{
ll i=x;
while(i<=n){c2[i]+=y;i=i+(i&(-i));}
return 0;
}
ll sum(ll x)
{
return find1(x)*x-find2(x);
}
int main()
{
ll i,j,v;
scanf("%lld %lld\n",&n,&m);
for(i=1;i=1)
{
scanf("%c ",&type);
if(type=='C')
{
scanf("%lld %lld %lld\n",&x,&y,&k);
change2(x,k*(x-1));change2(y+1,-k*(y+1-1));
change1(x,k);change1(y+1,-k);
}
else
{
scanf("%lld %lld\n",&x,&y);
printf("%lld\n",sum(y)-sum(x-1));
}
m--;
}
return 0;
}
树状数组的常数很小,代码很短,但是处理的范围窄。在做一些有关求和问题的时候不妨用一用。