树状数组log(N)的良好性能广泛适用于大数据的区间和修改,例如代码仓库查询
通过构建A[i]和C[i]对数据进行树状修改
A[i]:储存数据
C[i]:存放了A[0]~A[i]的和,C[x]母节点为C[x+lowbit(x)]
#define lowbit(x) (x&(-x))
单点修改&&更新后缀和:
void add(int x,int k)
{
for (; x <= 200; x += lowbit(x)) c[x]+=k;
}
查询前缀和:
int ask(int x)
{
int s = 0;
for (; x > 0; x -= lowbit(x)) s += c[x];
return s;
}
例:求逆序数
利用桶的思想,每读入一个元素x查询0~x的桶有几个非空,利用传统数组复杂度为(n*n),所以可以利用树状数组,当数据小时,可以单纯的利用桶的思想和树状数组
#include "bits/stdc++.h"
using namespace std;
#define N 100
#define lowbit(x) (x&(-x))
int8_t c[N] = {0};
void add(int x, int k) {
for (; x <= N; x += lowbit(x))
c[x] += k;
}
int ask(int x) {
int s = 0;
for (; x > 0; x -= lowbit(x))
s += c[x];
return s;
}
int main() {
int x,n;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &x);
if (i == 0)
printf("%d", ask(x));//每次输入一个值,计算他之前的和
else
printf(" %d", ask(x));
add(x + 1, 1);//放入桶
}
printf("\n");
}
离散化
通过离散化能将大数化小,减少时空占用,例如:
原数组:5 , 9e3 , 3e7 , -10
离散化:2,3,4,1
#include "bits/stdc++.h"
using namespace std;
#define N 1000000
int a[N],b[N];
int main()
{
int m;
scanf("%d",&m);
for(int i=0; i<m; i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(a,a+m);
int cnt=unique(a,a+m)-a; //离散化后的长度(去重)
for(int i=0; i<m; i++) {
b[i] = lower_bound(a, a + cnt, b[i]) - a + 1; //b[i]离散化后对应的值
printf("%d ",b[i]);
}
printf("%d",cnt);
}
离散化+树状数组解决逆序数
#include "bits/stdc++.h"
using namespace std;
#define N 500005
#define lowbit(x) (x&(-x))
long long c[N] = {0};
void lisan(int *a, int *b, int a_len) {
for (int i = 0; i < a_len; i++) {
b[i] = a[i];
}
sort(a, a + a_len);
int cnt = unique(a, a + a_len) - a; //离散化后的长度(去重)
for (int i = 0; i < a_len; i++) {
b[i] = lower_bound(a, a + cnt, b[i]) - a + 1; //b[i]离散化后对应的值
}
}
void add(int x, int k) {
for (; x <= N; x += lowbit(x))
c[x] += k;
}
int ask(int x) {
int s = 0;
for (; x > 0; x -= lowbit(x))
s += c[x];
return s;
}
int main() {
int a[N], b[N], m;
scanf("%d", &m);
for (int i = 0; i < m; i++) {
scanf("%d", &a[i]);
}
lisan(a, b, m);
int x;
long long sum = 0;
for (int i = 0; i < m; i++) {
x = b[i];
sum += i - ask(x);
add(x, 1);//放入桶
}
printf("%lld\n", sum);
}
树状数组进阶
将C[I]定义为A[I]-A[I-1]那么想要修改一个区间内的值,复杂度降为logn而非nlogn
#include "bits/stdc++.h"
using namespace std;
#define N 500010
#define lowbit(x) (x&(-x))
long long c1[N] = {0},c2[N]={0};
void lisan(long long *a, long long *b, long long a_len) {
for (long long i = 0; i < a_len; i++) {
b[i] = a[i];
}
sort(a, a + a_len);
long long cnt = unique(a, a + a_len) - a; //离散化后的长度(去重)
for (long long i = 0; i < a_len; i++) {
b[i] = lower_bound(a, a + cnt, b[i]) - a + 1; //b[i]离散化后对应的值
}
}
void add(long long x, long long k) { //差分更新
for (long long i=x; i <= N; i += lowbit(i)) {
c1[i]+= k;
c2[i]+=k*(x-1);
}
}
void add_point(long long x,long long k)//单点更新
{
add(x,k);
add(x+1,-k);
}
void add_block(long long a,long long b,long long k) {//区间更新
add(a,k);
add(b+1,-k);
}
long long ask(long long x) { //区间查询
long long s = 0;
for (long long i=x; i > 0; i -= lowbit(i))
{s+= x*c1[i]-c2[i];}
return s;
}
int main() {
long long n, m;
scanf("%lld%lld", &n,&m);
long long sum = 0;
long long a[n+1]={0};
for (long long i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
add(i, a[i]-a[i-1]);
}
for(long long i=0;i<n;i++)
{
long long d;
scanf("%lld",&d);
if(d==1)
{
long long c;
long long a,b;
scanf("%lld%lld%lld",&a,&b,&c);
add_block(a,b,c);
}
else
{
long long a;
scanf("%lld",&a);
printf("%lld\n",ask(a)-ask(a-1));
}
}
/* long long a[n]={0},b[n]={0};
for(long long i=0;i<n;i++)
{
scanf("%lld",&a[i]);
}
lisan(a,b,n);
for (long long i =0; i<n; i++) {
long long x=b[i];
sum += i - ask(x+1);
add_polong long(x+1,1);
}
prlong longf("%lld\n",sum);
*/
}