您的位置:首页 > 其它

树状数组

2008-12-30 17:42 281 查看

树状数组





  树状数组是一个查询和修改复杂度都为log(n)的数据结构,假设数组a[1..n],

  那么查询a[1]+...+a的时间是log级别的,而且是一个在线的数据结构,

  支持随时修改某个元素的值,复杂度也为log级别。

  来观察这个图:

  令这棵树的结点编号为C1,C2...Cn。令每个结点的值为这棵树的值的总和,那么容易发现:

  C1 = A1

  C2 = A1 + A2

  C3 = A3

  C4 = A1 + A2 + A3 + A4

  C5 = A5

  C6 = A5 + A6

  C7 = A7

  C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8

  ...

  C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16

  这里有一个有趣的性质,下午推了一下发现:

  设节点编号为x,那么这个节点管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素。因为这个区间最后一个元素必然为Ax,

  所以很明显:Cn = A(n – 2^k + 1) + ... + An

  算这个2^k有一个快捷的办法,定义一个函数如下即可:

  int lowbit(int x){

  return x&(x^(x–1));

  }

  当想要查询一个SUM(n)时,可以依据如下算法即可:

  step1: 令sum = 0,转第二步;

  step2: 假如n <= 0,算法结束,返回sum值,否则sum = sum + Cn,转第三步;

  step3: 令n = n – lowbit(n),转第二步。

  可以看出,这个算法就是将这一个个区间的和全部加起来,为什么是效率是log(n)的呢?以下给出证明:

  n = n – lowbit(n)这一步实际上等价于将n的二进制的最后一个1减去。而n的二进制里最多有log(n)个1,所以查询效率是log(n)的。

  那么修改呢,修改一个节点,必须修改其所有祖先,最坏情况下为修改第一个元素,最多有log(n)的祖先。

  所以修改算法如下(给某个结点i加上x):

  step1: 当i > n时,算法结束,否则转第二步;

  step2: Ci = Ci + x, i = i + lowbit(i)转第一步。

  i = i +lowbit(i)这个过程实际上也只是一个把末尾1补为0的过程。

  对于数组求和来说树状数组简直太快了!

以上资料来自baidu百科

PKU有2题树状数组的题

PKU 1195 Mobile phones(2维的~) http://acm.pku.edu.cn/JudgeOnline/problem?id=1195
PKU 3321 Apple Tree(结合图来求解) http://acm.pku.edu.cn/JudgeOnline/problem?id=3321
3321需要先o(n)的dfs处理以后才可以使用树状数组
还有就是需要自己写表来构树。。vector=TLE....

lowbit这么写也可以: x&(x^(x-1))
#include<iostream>
#include<vector>
using namespace std;
#define lowbit(x) ((x)&(~(x)+1))
struct Node
{
int cnt,id;
}node[100001];
int n,ans[1000001],id,C[1000001],sum[1000001],BUF;
struct Next
{
int next,val;
}next[2000001];
bool flag[1000001];
int dfs(int x)
{
int i,tmp,cid,nex;
flag[x]=true;
tmp=node[x].cnt;
for(nex=next[x].next;nex!=-1;nex=next[nex].next)
if(!flag[next[nex].val])tmp+=dfs(next[nex].val);
++id;
node[x].id=id;
return ans[id]=tmp;
}
int gsum(int x)
{
int ret=0;
while(x>0)
{
ret+=C[x];
x-=lowbit(x);
}
return ret;
}
void update(int x,int val)
{
while(x<=n)
{
C[x]+=val;
x+=lowbit(x);
}
}
int main()
{
int a,b,i,m,tval,nex;
char c;
while(scanf("%d",&n)!=EOF)
{
for(i=1;i<=n;i++)node[i].cnt=1;
memset(ans,0,sizeof(ans));
memset(flag,false,sizeof(flag));
for(i=1;i<=n;i++)next[i].next=-1;
BUF=1000001;
id=0;
for(i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
nex=a;
next[a].val=a;
while(nex!=-1)
{
if(next[nex].next==-1)break;
nex=next[nex].next;
}
next[nex].next=++BUF;
next[BUF].val=b;
next[BUF].next=-1;
nex=b;
next[b].val=b;
while(nex!=-1)
{
if(next[nex].next==-1)break;
nex=next[nex].next;
}
next[nex].next=++BUF;
next[BUF].val=a;
next[BUF].next=-1;
}
dfs(1);
memset(sum,0,sizeof(sum));
for(i=1;i<=n;i++)sum[i]=i;
for(i=1;i<=n;i++)C[i]=sum[i]-sum[i-lowbit(i)];
scanf("%d%*c",&m);
while(m--)
{
scanf("%c %d%*c",&c,&id);
if(c=='Q')
{
printf("%d/n",gsum(node[id].id)-gsum(node[id].id-ans[node[id].id]));
}
else
{
tval=(node[id].cnt==1?-1:1);
node[id].cnt=(node[id].cnt==0?1:0);
update(node[id].id,tval);
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: