树状数组
来源:互联网 发布:搞笑淘宝买家秀图对比 编辑:程序博客网 时间:2024/05/02 03:43
树状数组
树状数组是一个查询和修改复杂度都为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;
- }
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- Team Foundation Server 2008的几个常用命令
- 2008年总结。
- hibernate+oracle+clob的读写
- 利用ajax技术显示上传进度
- MySql数据库的列类型(字段类型)
- 树状数组
- 5分钟让你明白融危机爆发的原因
- 线程安全与线程不安全
- 短信猫信息开发错误
- 如何在 Visual C++ 中使用 STL 队列类的该成员函数
- VC 工程文件一般解析
- 不出桌面,单任务运行点歌系统
- Tomcat研究之ClassLoader
- 俞敏洪谈大学生就业 (荐)