树状数组
Posted darlingroot
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树状数组相关的知识,希望对你有一定的参考价值。
很久之前写过一篇
只不过很短(我太弱了)
倒还基础
1.单点查询+区间修改
#include<cstdio> using namespace std; int n,m,u,x,y,z; int a[500005]; int lowbit(int c) { return c &(-c); } void update(int c,int d) { while(c <= n) { a[c] += d; c += lowbit(c); } } int query(int c) { int sum = 0; while(c > 0) { sum += a[c]; c -= lowbit(c); } return sum; } int main() { scanf("%d%d",&n,&m); for(int i = 1; i <= n; i++) { scanf("%d",&u); update(i,u); } for(int i = 1; i <= m; i++) { scanf("%d%d%d",&x,&y,&z); if(x == 1) update(y,z); if(x == 2) printf("%d\\n",query(z) - query(y-1)); } return 0; }
2.区间修改+单点查询
差分的思想
查询
设原数组为a[ i ],
设数组tree[ i ] = tree[ i ] - tree[i - 1](a[ 0 ] = 0)
则a[ i ] = tree[1] + tree[2] +...tree[ i ];
修改
当给区间[ l,r ]加上x的时候时
a[ l ]与前一个元素a[l - 1]的差增加了x
a[r + 1]与a[r]的差减少了x
根据tree[ i ]数组的定义
只需要给a[ l ]加上x
给a[r + 1]减去x即可
#include <cstdio> #define lowbit(x) x & -x using namespace std; int tree[500005]; int n, m; void add(int x,int num) { while (x <= n) { tree[x] += num; x += lowbit(x); } } int query(int x) { int ans = 0; while (x) { ans += tree[x]; x -= lowbit(x); } return ans; } int main() { scanf("%d%d", &n, &m); int last = 0, now; for (int i = 1; i <= n; i++) { scanf("%d", &now); add(i, now - last); last = now; } int flg; while (m--) { scanf("%d", &flg); if (flg == 1) { int x, y; int k; scanf("%d%d%d", &x, &y, &k); add(x, k); add(y + 1, -k); } else if (flg == 2) { int x; scanf("%d", &x); printf("%d\\n", query(x)); } } return 0; }
3.区间修改+区间查询
差分的思想
查询
位置p的前缀和即:(p + 1)* sum1数组中p的前缀和 - sum2数组中p的前缀和
区间[ l , r]的和即:位置r的前缀和 - 位置l - 1的前缀和
修改
对于sum1数组的修改同2中d数组的修改
对于sum2数组的修改也类似,给sum2[ l ]加上l * x,给sum2[r + 1]减去(r + 1)* x
#include<cstdio> #include<algorithm> #define ll long long using namespace std; inline ll read() { ll sum = 0,p = 1; char ch = getchar(); while(ch < ‘0‘ || ch > ‘9‘) { if(ch == ‘0‘) p = -1; ch = getchar(); } while(ch >= ‘0‘ && ch <= ‘9‘) { (sum *= 10) += ch - ‘0‘; ch = getchar(); } return sum * p; } const ll maxn = 1e5 + 5 , maxm = 1e5 + 5; ll n,m; ll v[maxn],sum1[maxn],sum2[maxn]; ll lowbit(ll o) { return o&(-o); } void update(ll x,ll k) { int i = x; while(x <= n) { sum1[x] += k; sum2[x] += i * k; x += lowbit(x); } } ll query(ll o) { ll ans = 0; for(ll i = o;i;i -= lowbit(i)) ans += (o + 1) * sum1[i] - sum2[i]; return ans; } int main() { n = read(),m = read(); for(ll i = 1;i <= n;i++) { v[i] = read(); update(i,v[i] - v[i - 1]); } for(ll i = 1;i <= m;i++) { ll opt = read(),x,y,k; if(opt == 1) { x = read(),y = read(),k = read(); update(x,k); update(y + 1,-k); } if(opt == 2) { x = read(),y = read(); printf("%lld\\n",query(y) - query(x - 1)); } } return 0; }
二维树状数组
运用类比的思想(呕自己
一维树状数组中tree[x]中记录的是右端点为x、长度为lowbit(x)的区间的区间和
二维树状数组中,定义tree[x][y],记录的是右下角为(x,y),高为lowbit(x),宽为lowbit(y)的区间的区间和
一、单点修改+区间查询
//单点修改+区间查询
void update(int x,int y,int z)//将点(x,y)加上z
{
int mrk = y;
while(x <= n)
{
y = mrk;
while(y <= n)
{
tree[x][y] += z;
y += lowbit(y);
}
x += lowbit(x);
}
}
void query(int x,int y)//查询左上角为(1,1),右下角为(x,y)的矩阵和
{
int ans = 0,mrk = y;
while(x)
{
y = mrk;
while(y)
{
ans += tree[x][y];
y -= lowbit(y);
}
x -= lowbit(x);
}
}
二、区间修改+单点查询
“差分”思想
由二维前缀和:
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j]
可得
差分数组d[i][j] 为 a[i][j] 与 a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1]的差
举个例子
下面这个二位数组
1 4 8 6 7 2 3 9 5
对应的差分数组为
1 3 4 5 -2 -9 -3 5 1
给最中间的3*3矩阵加上x时
差分数组的变化:
0 0 0 0 0 0 +x 0 0 -x 0 0 0 0 0 0 0 0 0 0 0 -x 0 0 +x
这样修改差分,效果为:
0 0 0 0 0 0 x x x 0 0 x x x 0 0 x x x 0 0 0 0 0 0
//二维树状数组:区间修改+单点查询 void update(int x,int y,int z) { int mrk = y; while(x <= n) { y = mrk; while(y <= n) { tree[x][y] += z; y += lowbit(y); } x += lowbit(x); } } void range_update(int xa,int ya,int xb,int yb,int z) { update(xa,ya,z); update(xa,yb+1,-z); update(xb+1,ya,-z); update(xb+1,yb+1,z); } void query(int x,int y) { int mrk = y,ans = 0; while(x) { y = mrk; while(y) { ans += tree[x][y]; y -= lowbit(y); } x -= lowbit(x); } }
三、区间修改+区间查询
//二维树状数组:区间修改+区间查询 #include<cstdio> #include<algorithm> #define ll long long using namespace std; inline ll read() { ll sum = 0,p = 1; char ch = getchar(); while(ch < ‘0‘ || ch > ‘9‘) { if(ch == ‘-‘) p = -1; ch = getchar(); } while(ch >= ‘0‘ && ch <= ‘9‘) { (sum *= 10) += ch - ‘0‘; ch = getchar(); } return sum * p; } const int N = 205; ll n,m,Q; ll t1[N][N],t2[N][N],t3[N][N],t4[N][N]; ll lowbit(ll o) { return o & -o; } void update(ll x,ll y,ll z) { for(int X = x;X <= n;X += lowbit(X)) for(int Y = y;Y <= m;Y += lowbit(Y)) { t1[X][Y] += z; t2[X][Y] += z * x; t3[X][Y] += z * y; t4[X][Y] += z * x * y; } } void range_update(ll xa,ll ya,ll xb,ll yb,ll z) { update(xa,ya,z); update(xa,yb + 1,-z); update(xb + 1,ya,-z); update(xb + 1,yb + 1,z); } ll query(ll x,ll y) { ll ans = 0; for(int i = x;i;i -= lowbit(i)) for(int j = y;j;j -= lowbit(j)) ans += (x + 1) * (y + 1) * t1[i][j] - (y + 1) * t2[i][j] - (x + 1) * t3[i][j] +t4[i][j]; return ans; } ll range_query(ll xa,ll ya,ll xb,ll yb) { return query(xb, yb) - query(xb, ya - 1) - query(xa - 1, yb) + query(xa - 1, ya - 1); } int main() { n = read(),m = read(),Q = read(); for(int i = 1;i <= n;i++) for(int j = 1;j <= m;j++) { ll z = read(); range_update(i,j,i,j,z); } while(Q--) { ll ya = read(),xa = read(),yb = read(),xb = read(),z = read(),a = read(); if(range_query(xa,ya,xb,yb) < z * (xb - xa + 1) * (yb - ya + 1)) range_update(xa,ya,xb,yb,a); } for(int i = 1;i <= n;i++) { for(int j = 1;j <= m;j++) printf("%lld",range_query(i,j,i,j)); printf("\\n"); } return 0; }
以上是关于树状数组的主要内容,如果未能解决你的问题,请参考以下文章