树状数组
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;
}
相关文章推荐
- hdu 3450(树状数组优化dp)
- Codeforces 652D Nested Segments【离散化+思维+树状数组】
- HDU 4046 Panda 树状数组
- UVA 12086 Potentiometers(树状数组)
- 【树状数组】The 36th ACM/ICPC Asia Regional Beijing Site Online Contest - G Panda
- 树状数组 求逆序数 poj 2299 离散化
- hdu 4046 BF+树状数组
- HDU2838 Cow Sorting (树状数组)
- LeetCode 307 Range Sum Query - Mutable(树状数组 || 线段树)
- POJ 2464 Brownie Points II(树状数组||线段树)
- lightoj 1080 - Binary Simulation(树状数组)
- 树状数组
- cf Babaei and Birthday Cake(树状数组解决LIS问题)
- 树状数组求逆序数
- 树状数组算法分析
- POJ 3067 —— 树状数组求逆序对
- poj3067 Japan 树状数组求逆序对
- 树状数组总结
- 树状数组
- hdu 1166 敌兵布阵【线段树Or树状数组】