研究了很长时间的树状数组,下面来做一个自己的总结。(如果以下见解有什么问题,欢迎指出)
树状数组是真的很有意思,根据我现在的理解,如果普普通通的不做任何东西,那么他的单点修改适用于区间查询,他的区间修改,适用于单点查询。
如果想要区间修改区间查询,就得费一点功夫了。
一 :单点修改,区间查询
这是树状数组一个比较常规的用法,不再详细解释。
int low_bit(int x)
{
return x&(-x);
}
void add(int i,int c)
{
while(i<=n) //n是一共有多少点
{
a[i]+=c;
i+=low_bit(i);
}
}
int q(int i)
{
int ans=0;
while(i>0)
{
ans+=a[i];
i-=low_bit(i);
}
return ans;
}
int main()
{
add(i,c); // i是对那个点增加 c 是要增加的多少
//若查询 l--r 区间的和
ans=q(r)-q(l-1);
}
二 :单点修改,单点查询
这问题挺傻逼的,这东西直接用数组就行了,没必要再用树状数组了。
三 :区间修改,单点查询
这也是树状数组一个比较常规的用法,不再详细解释
int low_bit(int x)
{
return x&(-x);
}
void add(int i,int c)
{
while(i<=n) //n是一共有多少点
{
a[i]+=c;
i+=low_bit(i);
}
}
int q(int i)
{
int ans=0;
while(i>0)
{
ans+=a[i];
i-=low_bit(i);
}
return ans;
}
int main()
{
add(l,c)
add(r+1,-c)
//对l--r区间加 c
q(i) //查询i点的值
}
四 :区间修改,区间查询
这可能也是比较难理解的地方了。
但是认真仔细的看一看,还是很好理解的。
这也是,我看别人的博客,才知道的。
首先我们设原来的数组是a[i],a[i]里保存的是前i个元素的值总和。
然后设C1,C1代表的是a[i]-a[i-1],那么就是第i个元素的值;
我们假设sigma(r,i),表示r数组前i项的和。
网上有这样一个式子,其实这个式子的误导性很大。
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]) (式子①)
那么我们就维护一个数组c2[n],其中c2[i] = (i-1)*c[i]
式子①=n*sigma(c,n) - sigma(c2,n)
下面我们看一道题,来展示一下代码。
给你N个数,有两种操作:
1:给区间[a,b]的所有数增加X
2:询问区间[a,b]的数的和。
第一行一个正整数n,接下来n行n个整数,
再接下来一个正整数Q,每行表示操作的个数,
如果第一个数是1,后接3个正整数,
表示在区间[a,b]内每个数增加X,如果是2,
表示操作2询问区间[a,b]的和是多少。
pascal选手请不要使用readln读入
对于每个询问输出一行一个答案
3
1
2
3
2
1 2 3 2
2 2 3
9
数据范围
1<=n<=200000
1<=q<=200000
题目来源:点击打开链接
#include
#define lowbit(x) (x&-x)
#define ll long long
#define maxn 200010
using namespace std;
ll n, q, c1[maxn], c2[maxn], num[maxn];
void add(ll *r, ll pos, ll v)
{for(;pos<=n;pos+=lowbit(pos))r[pos]+=v;}
ll sigma(ll *r, ll pos)
{
ll ans;
for(ans=0;pos;pos-=lowbit(pos))ans+=r[pos];
return ans;
}
int main()
{
ll i, j, type, a, b, v, sum1, sum2;
scanf("%lld",&n);
for(i=1;i<=n;i++)
{
scanf("%lld",num+i);
add(c1,i,num[i]-num[i-1]);
add(c2,i,(i-1)*(num[i]-num[i-1]));
}
scanf("%lld",&q);
while(q--)
{
scanf("%lld",&type);
if(type==1)
{
scanf("%lld%lld%lld",&a,&b,&v);
add(c1,a,v);add(c1,b+1,-v);
add(c2,a,v*(a-1));add(c2,b+1,-v*b);
}
if(type==2)
{
scanf("%lld%lld",&a,&b);
sum1=(a-1)*sigma(c1,a-1)-sigma(c2,a-1);
sum2=b*sigma(c1,b)-sigma(c2,b);
printf("%lld\n",sum2-sum1);
}
}
return 0;
}