树状数组,是一种设计新颖的数组结构,它能够高效地获取数组中的连续n个数的和。
概括说,树状数组通常用于解决以下问题:
在数组{a}中的元素可能不断的被修改下,快速获取连续几个数的和。
定义:
对于序列a,我们设一个数组C
k为i在二进制下末尾0的个数2^k就是i 保留最右边的1,其余位全变0i从1开始算!
C即为a的树状数组。
对于i,如何求2^k ?
其实答案就是 2^k=i & (i^(i-1)) 也就是 i&(-i)。
lowbit(x)=x&(-x)
lowbit(x) 实际上就是x的二进制表示形式留下最右边的1,其他位都变成0。
所以树状数组的表达是可以表示为: C[i]= a[i-lowbit(i)+1] + …+ a[i]
C1 = a1 Sum[1] = C1
C2 = a1+a2 Sum[2] = C2
C3 = a3 Sum[3] = C2+C3
C4 = a1+a2+a3+a4 Sum[4] = C4
C5 = a5 Sum[5] = C4+C5
C6 = a5+a6 Sum[6] = C4+C6
C7 = a7 Sum[7] = C4+C6+C7
C8 = a1+a2+a3+a4+a5+a6+a7+a8 Sum[8] = C8
……
C2^n = a1+a2+….+a2^n
Sum[]存放的就是某段区间的和。
这样存好数组后就可以快速的得到某段区间的和:
单点更新:
简单的树状数组题:
用树状数组求逆序数:
#include <iostream>
#include <cstring>
using namespace std;
int n,c[1001];
int lowbit(int x)
{
x&=-x;
return x;
}
int sum(int x)
{
int ret = 0;
while(x > 0)
{
ret += c[x];
x -= lowbit(x);
}
return ret;
}
void add(int x,int d)
{
while(x <= n)
{
c[x] += d;
x += lowbit(x);
}
}
int main()
{
int i;
while(cin>>n)
{
int ans = 0;
memset(c,0,sizeof(c));
for(i = 1; i <= n; i++)
{
int a;
cin>>a;
add(a,1);
ans += (i-sum(a));
}
cout<<ans<<endl;
}
return 0;
}
hdu 1394
//对数组a[],每次将数组的第一个元素放到数组末尾,求所有序列的逆序数中最小的一个
//先用数状数组求原数组的逆序数的个数ans,将a[i]放到数组后的逆序数的个数 ans = ans + (n-a[i]) - (a[i]-1) (将a[i]移到数组后面,即会有(n-a[i])个数比a[i]大,且有(a[i]-1)个数比a[i]小),然后比较出最小值即可
#include <iostream>
#include <cstring>
using namespace std;
int n,c[5001];
int min(int a,int b)
{
return a < b ? a : b;
}
int lowbit(int x)
{
x&=-x;
return x;
}
int sum(int x)
{
int ret = 0;
while(x > 0)
{
ret += c[x];
x -= lowbit(x);
}
return ret;
}
void add(int x,int d)
{
while(x <= n)
{
c[x] += d;
x += lowbit(x);
}
}
int main()
{
int i;
while(cin>>n)
{
int ans = 0;
memset(c,0,sizeof(c));
int a[5005];
for(i = 1; i <= n; i++)
{
cin>>a[i];
a[i]++;
add(a[i],1);
ans += (i-sum(a[i]));
}
int ans1 = ans;
for(i = 1; i <= n; i++)
{
ans += ((n-a[i])-(a[i]-1));
ans1 = min(ans1,ans);
}
cout<<ans1<<endl;
}
return 0;
}
//hdu 2838
//树状数组求逆序数
//题目描述:给定n个数字保证唯一的序列,如果两个数a[i],a[j]要交换,那么代价为a[i]+a[j]
//求让他们变成升序的最小代价
//分析:其实这个结果和逆序数有关,对某个位置i,如果前面比他大的有x个,那么a[i]至少要加x次
//如果后面有y个比a[i]小,那么a[i]至少要加y次,也就是说用两个树状数组来分别维护当前位置时前面有多少比他大
//后面有多少个比他小
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const __int64 MAX = 100005;
__int64 c[MAX],A[MAX];
__int64 lowbit(__int64 x)
{
return x&-x;
}
void add(__int64 x,__int64 d,__int64 k)
{
while(x <= MAX)
{
c[x]+=d;
A[x]+=k;
x+=lowbit(x);
}
}
__int64 getsum(__int64 x)
{
__int64 ret = 0;
while(x > 0)
{
ret+=c[x];
x-=lowbit(x);
}
return ret;
}
__int64 getsum1(__int64 x)
{
__int64 ret = 0;
while(x > 0)
{
ret+=A[x];
x-=lowbit(x);
}
return ret;
}
int main()
{
__int64 n,i,a;
while(~scanf("%I64d",&n))
{
memset(c,0,sizeof(c));
memset(A,0,sizeof(A));
__int64 ans = 0;
for(i = 1; i <= n; i++)
{
scanf("%I64d",&a);
add(a,1,a);
__int64 num = i - getsum(a);
__int64 num1 = 0;
if(num!=0)
{
num1 = getsum1(MAX) - getsum1(a);
}
ans += num*a + num1;
}
printf("%I64d\n",ans);
}
return 0;
}
//HDU1892 二维树状数组
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int n,m;
int c[1005][1005],A[1005][1005];
int lowbit(int x)
{
return x&-x;
}
void add(int i,int j,int d)
{
A[i][j]+=d;
for(int x = i; x <= n; x+=lowbit(x))
for(int y = j; y <= m; y+=lowbit(y))
c[x][y]+=d;
}
int getsum(int i,int j)
{
int ret = 0;
for(int x = i; x > 0; x-=lowbit(x))
for(int y = j; y > 0; y-=lowbit(y))
ret+=c[x][y];
return ret;
}
int main()
{
int t,q,i,j,temp = 1;
n = m = 1001;
scanf("%d",&t);
while(t--)
{
printf("Case %d:\n",temp++);
scanf("%d",&q);
memset(A,0,sizeof(A));
memset(c,0,sizeof(c));
for(i = 1; i <= n; i++)
for(j = 1; j <= m; j++)
{
add(i,j,1);
}
while(q--)
{
char ch1;
getchar();
scanf("%c",&ch1);
if(ch1=='S')//输出小矩阵中的值的和
{
int x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
x1++;
y1++;
x2++;
y2++;
int mx = max(x1,x2);
int nx = min(x1,x2);
int my = max(y1,y2);
int ny = min(y1,y2);
int num = getsum(mx,ny-1) + getsum(nx-1,my) - getsum(nx-1,ny-1);
printf("%d\n",getsum(mx,my) - num);
}
else if(ch1=='A')//点(x1,y1)的值加上k;
{
int x1,y1,k;
scanf("%d%d%d",&x1,&y1,&k);
add(x1+1,y1+1,k);
}
else if(ch1=='D')//点(x1,y1)的值减去k;
{
int x1,y1,k;
scanf("%d%d%d",&x1,&y1,&k);
if(k > A[x1+1][y1+1]) k = A[x1+1][y1+1];
add(x1+1,y1+1,-k);
}
else if(ch1=='M')//从点(x1,y1)中取出k加入到(x2,y2)中
{
int x1,x2,y1,y2,k;
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&k);
if(k > A[x1+1][y1+1]) k = A[x1+1][y1+1];
add(x1+1,y1+1,-k);
add(x2+1,y2+1,k);
}
}
}
return 0;
}
//hdu 1559 最大子矩阵 (二维树状数组)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
int n,m;
int c[1002][1002];
int lowbit(int x)
{
return x&-x;
}
void add(int i,int j,int k)
{
for(int x = i; x <= n; x+=lowbit(x))
for(int y = j; y <= m; y+=lowbit(y))
c[x][y]+=k;
}
int getsum(int i,int j)
{
int ret = 0;
for(int x = i; x > 0; x-=lowbit(x))
for(int y = j; y > 0; y-=lowbit(y))
ret+=c[x][y];
return ret;
}
int main()
{
int t,i,j,x,y;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d%d",&n,&m,&x,&y);
x--;
y--;
memset(c,0,sizeof(c));
int r;
for(i = 1; i <= n; i++)
for(j = 1; j <= m; j++)
{
scanf("%d",&r);
add(i,j,r);
}
int sum = 0;
int ans = 0;
for(i = 1; i <= n-x; i++)
for(j = 1; j <= m-y; j++)
{
int nu = getsum(i+x,j-1) + getsum(i-1,j+y) - getsum(i-1,j-1);
sum = getsum(i+x,j+y) - nu;
ans = sum > ans ? sum : ans;
}
printf("%d\n",ans);
}
return 0;
}
//hdu 3743 树状数组+离散化
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAX = 1000005;
int n,c[MAX];
struct node
{
int x,num;
}a[MAX];
bool cmp(node a1,node a2)
{
if(a1.x < a2.x) return true;
return false;
}
bool cmp1(node a1,node a2)
{
if(a1.num < a2.num) return true;
return false;
}
int lowbit(int x)
{
return x&-x;
}
void add(int x,int d)
{
while(x <= n)
{
c[x]+=d;
x+=lowbit(x);
}
}
int getsum(int x)
{
int ret = 0;
while(x > 0)
{
ret+=c[x];
x-=lowbit(x);
}
return ret;
}
int main()
{
int i;
while(scanf("%d",&n)!=EOF)
{
for(i = 0; i < n; i++)
{
scanf("%d",&a[i].x);
a[i].num = i+1;
}
sort(a,a+n,cmp);//按值排序
for(i = 0; i < n; i++)
a[i].x = i+1;
sort(a,a+n,cmp1);//按原数组顺序排序
__int64 ans = 0;
memset(c,0,sizeof(c));
for(i = 1; i <= n; i++)
{
add(a[i-1].x,1);
ans+=i-getsum(a[i-1].x);
}
printf("%I64d\n",ans);
}
return 0;
}
//hdu 2852 树状数组 求比a大的第k个数(二分查找)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAX = 100005;
int c[MAX],A[MAX];
int lowbit(int x)
{
return x&-x;
}
void add(int x,int d)
{
if(A[x]+d < 0)printf("No Elment!\n");
else
{
A[x]+=d;
while(x <= MAX)
{
c[x]+=d;
x+=lowbit(x);
}
}
}
int getsum(int x)
{
int ret = 0;
while(x > 0)
{
ret+=c[x];
x-=lowbit(x);
}
return ret;
}
void find(int a,int k)
{
int l = a;
int r = MAX;
int mina = getsum(a);
while(l<=r)
{
int mid = (l+r)/2;
int e = getsum(mid) - mina;
if(e>=k)
{
if(e-A[mid] < k)
{
printf("%d\n",mid);
return;
}
r = mid-1;
}
if(e < k)
l = mid+1;
}
printf("Not Find!\n");
}
int main()
{
int m,p,e,a,k;
while(~scanf("%d",&m))
{
memset(c,0,sizeof(c));
memset(A,0,sizeof(A));
while(m--)
{
scanf("%d",&p);
if(p==0)
{
scanf("%d",&e);
add(e,1);
}
else if(p==1)
{
scanf("%d",&e);
add(e,-1);
}
else if(p==2)
{
scanf("%d%d",&a,&k);
if(getsum(MAX) - getsum(a) < k) printf("Not Find!\n");
else find(a,k);
}
}
}
return 0;
}