您的位置:首页 > 其它

一维树状数组~

2014-07-31 09:23 232 查看
       树状数组按照一定的规律来存储原数组的一些区间的和,以此可将查询和修改的复杂度都优化到O(logn),规律如下:


       从表面上看,树状数组C[i](1<=i<=n)中,当i为奇数的时候C[i]存的是A[i];当i为偶数时,如果i能表示为2的次幂则C[i]=A[1]+A[2]+···+A[i],如果i不能表示为2的次幂则C[i]=A[i-1]+A[i]。但还有更深层次的规律,其实C[i]=A[i-k+1]+A[i-k+2]+···+A[i](1<=i<=n);k=1<<x,x是i的二进制表示下右边0的个数,即

int lowbit(int i)
{
return i&(-i);
}
       lowbit()函数的返回值就是k。例如,当i=5时,int类型,4字节,占32位:

5的二进制为: 00000000 00000000 00000000 00000101

5的反码为:     11111111 11111111 11111111 11111010

-5的二进制为:11111111 11111111 11111111 11111011

lowbit(5)=1;所以,C[5]=A[5];

PS:

       在计算机中,负数以其正值的补码形式表达。

       反码表示法规定:正数的反码与原码相同,负数的反码为对该数的原码除符号位外各位取反。

       补码表示法规定:正数的补码与原码相同,负数的补码为对该数的原码除符号位外各位取反,然后在最后一位加1。

例题:NYOJ_士兵杀敌(二)

题意:插点问线

思路:向后修改,向前求和

CODE:

/**
**author :Or_me **
╭︿︿︿╮
{/ a  c /}
( (oo) )
︶︶︶
**    **
**NYOJ_116题**
** 2014 年 7月 31日**
**/
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cctype>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX=1000001;
int c[MAX];
int N,M;
int lowbit(int t)//lowbit(0)=0,会形成死循环,所以数组下标从1开始
{
return t&-t;
}
void Modify(int m,int n)//向后修改
{
for (int k=m; k<=N; k+=lowbit(k))
c[k]+=n;
}
int sum(int x)//向前求和
{
int ans=0;
for (int i=x; i>=1; i-=lowbit(i))
ans+=c[i];
return ans;
}
int getsum(int i,int j)
{
return sum(j)-sum(i-1);
}
int main()
{
int m,n,v;
char ch[7];
scanf("%d%d",&N,&M);
for (int i=1; i<=N; i++)
{
scanf("%d",&v);
Modify(i,v);
}
for (int i=1; i<=M; i++)
{
scanf("%s%d%d",ch,&m,&n);
if (ch[0]=='A')
Modify(m,n);
else if (ch[0]=='Q')
printf("%d\n",getsum(m,n));
}
return 0;
}

例题:NYOJ_士兵杀敌(四)

题意:插线问点

思路:向前修改,向后求和

CODE:

/**
**author :Or_me **
╭︿︿︿╮
{/ a c /}
( (oo) )
︶︶︶
** **
** 题**
** 2014 年 月 日**
**/
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cctype>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX=1000001;
int c[MAX];
int N,M,T;
int lowbit(int t)
{
return t&-t;
}
void Modify(int m,int n)//向前修改
{
for (int k=m; k>0; k-=lowbit(k))
c[k]+=n;
}
int sum(int x)//向后求和
{
int ans=0;
for (int i=x; i<=M; i+=lowbit(i))
ans+=c[i];
return ans;
}
int main()
{
int a,b,t,x;
char ch[7];
memset(c,0,sizeof(c));
scanf("%d%d",&T,&M);
for (int i=1; i<=T; i++)
{
scanf("%s",ch);
if (ch[0]=='A')
{
scanf("%d%d%d",&a,&b,&t);
Modify(a-1,-t);
Modify(b,t);
}
else
{
scanf("%d",&x);
printf("%d\n",sum(x));
}

}
return 0;
}
例题:NYOJ_士兵杀敌(五)
题意:插线问线

思路:树状数组离线版,查询在修改之后

CODE:

/**
**author :Or_me **
╭︿︿︿╮
{/ a c /}
( (oo) )
︶︶︶
** **
** 题**
** 2014 年 月 日**
**/
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cctype>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX=1000001;
int c[MAX];
int main()
{
int N,C,Q;
int a,b,x;
scanf("%d%d%d",&N,&C,&Q);
while (C--)
{
scanf("%d%d%d",&a,&b,&x);
c[a]+=x;
c[b+1]-=x;
}
for (int i=1;i<=N;i++)//得到每个士兵的战功
{
c[i]=c[i-1]+c[i];
}
for (int i=1;i<=N;i++)//得到前缀和
{
c[i]=(c[i-1]+c[i])%10003;//保存的是余数
}
while (Q--)
{
scanf("%d%d",&a,&b);
printf("%d\n",(c[b]-c[a-1]+10003)%10003);//+10003,防止结果小于0
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  树状数组