版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Tawn0000/article/details/81738773
树状数组专题
一直感觉树状数组用处比较小而且局限、因为最基本的用法就是单点更新和区间求和、但是线段树也能做,只不过代码长一点,但是仔细的去了解了一下树状数组以后发现有很多很赞的地方值得学习。
1.单点更新、区间求和
for(int x = i; x <= n; x += x&-x) c[x] ++; //a[i]单点更新加一
for(int x = r; x ; x -= x&-x) res += c[x]; //求前缀[0,r]
2.区间更新、单点查询
对于操作C L R d :在区间L-R上每个值增加d ,运用差分思想,建立一个初始全为0的b数组,b[L] += d; b[R+1] -= d;
for(int x = L; x <= n; x += x&-x) b[x] += d;
for(int x = R+1; x <= n; x -= x&-x) b[x] -= d;
对于操作Q i
int res = a[i];
for(int x = i; x ; x -= x&-x) res += b[x];
3.区间更新、区间查询
设S[x]为前缀和 c[x] = x*b[x]; 所以有
对于操作 C L R d b[L] += d; b[R+1] -= d; c[L] += L*d ; c[R+1] += R*d;
for(int x = L; x <= n; x += x&-x) b[x] += d, c[x] += L*d;
for(int x = R+1; x <= n; x += x&-x) b[x] -= d, c[x] -= (R+1)*d;
对于操作 Q L R
int res = s[r] - s[l-1];
for(int x = R; x ; x -= x&-x) res += (R+1)*b[x]-c[x];
for(int x = L-1; x ; x -= x&-x) res -= L*b[x] - c[x];
例题: POJ 3468 A Simple Problem with Intergers
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1e5+100;
const int INF = 0x3f3f3f3f;
typedef long long LL;
typedef pair<int,int> P;
#define PI 3.1415926
#define sc(x) scanf("%d",&x)
#define pf(x) printf("%d",x)
#define pfn(x) printf("%d\n",x)
#define pfs(x) printf("%d ",x)
#define rep(n) for(int i = 0; i < n; i++)
#define per(n) for(int i = n-1; i >= 0; i--)
#define mem(a,x) memset(a,x,sizeof(a))
int n,q;
LL b[maxn],c[maxn],s[maxn];
int a[maxn];
int main()
{
sc(n),sc(q);
mem(s,0LL);
rep(n) {sc(a[i+1]);s[i+1] = s[i] + a[i+1];}
mem(b,0LL);
mem(c,0LL);
rep(q)
{
char key[2];
int l,r,d;
scanf("%s%d%d",key,&l,&r);
if(key[0] == 'C')
{
sc(d);
for(int x = l;x <= n; x += x&-x) {b[x] += d;c[x] += l*d;}
for(int x = r+1;x <= n; x += x&-x) {b[x] -= d;c[x] -= (r+1)*d;}
}
else
{
LL res = s[r]-s[l-1];
//res = sum(r) - sum(l-1) s[x] = ssb[i] = (x+1)sb[i] - isb[i] = (x+1)sb[i] - sc[i];
for(int x = r; x ; x -= x&-x) res += ((r+1)*b[x] - c[x]);
for(int x = l-1; x ; x -= x&-x) res -= (l*b[x] - c[x]);
printf("%lld\n",res);
}
}
return 0;
}