前两天写的都是单点修改,区间查询的树状数组,今天来练习一下区间修改,单点查询的树状数组,本文适合学过单点修改,区间查询的朋友阅读。
首先简单介绍一下区间修改,单点查询与之前单点修改,区间查询的区别。
tr[N]
用来存储原数组的差分- lowbit函数,用来分层
int lowbit(int x)
{
return x & -x;
}
- 对某一点加一个数
void add(int x, int v)
{
for(int i=x;i<=n;i+=lowbit(i)) tr[i] += v;
}
- 某一点变成另外一个数
void add(int x, int v)
{
for(int i=x;i<=n;i+=lowbit(i)) tr[i] += v - tr[i];
//tr[i] = tr[i] - tr[i] + v;
}
- 求前缀和
int sum(int x)
{
int ans = 0;
for(int i=x;i!=0;i-=lowbit(i)) ans += tr[i];
return ans;
}
没错,你没有看错,这两种树状数组的基本函数并没有太大区别,区别在于,第二种的树状数组存的是差分而不是数本身。 为什么存差分呢?下面以这道题为例讲解。
模板题
P3368 【模板】树状数组 2
注意区别
在这里,我们的树状数组存的是差分!!!
原因如下:差分就是每两个数之间的差值,假设我们现在有原数据:a[1],a[2]...a[x]
那么原数据的差分相加就为:
(a[1]−0) + (a[2]−a[1]) + (a[3]−a[2]) + (a[4]−a[3]) +…+ (a[x]−a[x−1]) = a[x]
很巧妙的得到了a[x]
。
for (int i = 1; i <= n; i++)
{
cin >> a[i];
add(i, a[i] - c);
c = a[i];//差分
}
还有就是修改区间,也是差分的性质。
{
cin >> x >> y >> z;
add(x,z);
add(y+1,-z);//差分的性质
}
AC代码
int lowbit(int x)
{
return x & -x;
}
void add(int x, int v)
{
for (int i = x; i <= N; i += lowbit(i)) tr[i] += v;
}
int query(int x)
{
int res = 0;
for (int i = x; i != 0; i -= lowbit(i)) res += tr[i];
return res;
}
signed main()
{
int n, w, c = 0;
cin >> n >> w;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
add(i, a[i] - c);
c = a[i];//差分
}
while (w--)
{
int op,x,y,z;
cin >> op;
if (op == 1)
{
cin >> x >> y >> z;
add(x,z);
add(y+1,-z);//差分的性质
}
else
{
cin >> x;
cout << query(x) << endl;
}
}
return 0;
}
还有这道题,其实也是模板
P4939 Agent2
AC代码
const int N = 10000010;
int a[N],tr[N];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int v)
{
for (int i = x; i <= N; i += lowbit(i)) tr[i] += v;
}
int query(int x)
{
int res = 0;
for (int i = x; i != 0; i -= lowbit(i)) res += tr[i];
return res;
}
signed main()
{
int n, w, c = 0;
cin >> n >> w;
while (w--)
{
int op, x, y;
cin >> op;
if (op == 0)
{
cin >> x >> y;
add(x,1);
add(y+1,-1);//差分的性质
}
else
{
cin >> x;
cout << query(x) << endl;
}
}
return 0;
}