[Pku 2352 2155 Hdu 3584] 线段树(五) {树状数组}

{

就我学过的数据结构而言

最优美的数据结构是并查集

然后是树状数组

再次是散列表

......

}

优美的含义就是简明 精巧

当然特别强大的数据结构一般都不好写

比如Splay 线段树 平衡树之类

所以也不是很优美

线段树确实强大 但是在有些情况下

我们可以用树状数组实现线段树的一部分功能

 

通常意义上的树状数组是一个一维数组 我们一般用c[]来记录

相对的 还有一个原数组a[] 记录原有的序列 c[]是基于a[]而产生的

树状数组可以实现查询区间和修改单值的操作

这些操作线段树也可以实现 理论复杂度同样为O(Log2N)

但是树状数组的常数和编程复杂度更低

对于可以转化为这两个操作的问题 我们优先考虑树状数组

而且树状数组还有比线段树更好的可扩展性 可以轻易地扩展到高维

只需把通常意义上的树状数组改造成N维的树状数组即可

显然N维的线段树是相当恐怖的东西 相比而言 N维的树状数组就优美了许多

 

要理解树状数组 看几张图即可

具体的算法可以到baidu去找 三个函数才几行话{Lowbit(),Getsum(),Change()}

各人的理解方式可能不同 自己理解出来的才是最好的

提示一下 树状数组的核心是二进制思想

相信大家小时候都做过这个这个问题 任何一个数可以拆成不同的2的幂的和

树状数组通俗的讲就是这个道理

由于拆出的数的个数不超过Log2N个 树状数组的复杂度就是O(Log2N)

 

树状数组的入门问题

Pku 2352 http://poj.org/problem?id=2352

算法也很简单

把所有星星都投影到x轴上

由于给的星星是有顺序的(从下到上 从左到右)

每次插入一个星星 就累加当前的统计值 最后输出即可

 

const maxn=15000;
maxm
=32001;
var x,y:array[1..maxn]of longint;
c:
array[1..maxm]of longint;
h:
array[0..maxn-1]of longint;
i,n,m:longint;
function lowbit(v:longint):longint;
begin
lowbit:
=v and -v;
end;
procedure insert(x:longint);
var y:longint;
begin
y:
=x;
while y<=m do
begin
c[y]:
=c[y]+1;
y:
=y+lowbit(y);
end;
end;
function query(x:longint):longint;
var ans,y:longint;
begin
y:
=x;
ans:
=0;
while y>0 do
begin
ans:
=ans+c[y];
y:
=y-lowbit(y);
end;
query:
=ans;
end;
begin
assign(input,
'star.in'); reset(input);
assign(output,
'star.out'); rewrite(output);
readln(n);
m:
=0;
for i:=1 to n do
begin
readln(x[i],y[i]);
if m<x[i] then m:=x[i];
end;
m:
=m+1;
for i:=1 to n do
begin
inc(h[query(x[i]
+1)]);
insert(x[i]
+1);
end;
for i:=0 to n-1 do
writeln(h[i]);
close(input); close(output);
end.

 

 

树状数组很优美 但是这优美不是轻易能领略的 需要进行转化

原本树状数组支持的操作是修改一个数 查询区间和

我们有时候会碰到查询一个数 修改区间值的问题

同样可以通过转化来用树状数组解决

对于查询一个数a[i]我们转化为getsum(i) 即把a[i]转化为长为i前缀和

而修改区间我们如下图操作

修改[x,y] 我们就change(x,1) change(y+1,-1)

不难发现 对[x,y]内的数getsum就会+1 否则是不变的 这就相当于区间修改了

 

树状数组可以轻易的扩展到2维 3维

看两个这样的问题

Pku 2155 http://poj.org/problem?id=2155

Hdu 3584 http://acm.hdu.edu.cn/showproblem.php?pid=3584

分别是树状数组的二维和三维延伸

题意差不多

给定0 1阵 支持取反一个区间 查询单个值

对于取反操作 我们记录取反的次数 然后取2的模就可以反映当前的值了

这样问题就是支持区间修改和查询单值的问题

如果是一维的问题直接使用上面所说的方法就可以解决

现在是二维 三维了

二维的树状数组只是加了一维 操作只是多了一重循环

具体的操作可以baidu而知 不再赘述

我们可以如下图对c[][]进行修改 然后getsum(i,j)来求当前a[i,j]的值

三维需要画一个好一点的图 画出来了很有成就感 试一试吧

代码很好懂 具体看代码

Matrix

 

const maxn=1000;
var c:array[1..maxn,1..maxn]of longint;
i,x1,y1,x2,y2,z,n,m:longint;
ch:char;
function lowbit(x:longint):longint;
begin
lowbit:
=x and -x;
end;
procedure chg(x,y,z:longint);
var i,j:longint;
begin
i:
=x;
while i<=n do
begin
j:
=y;
while j<=n do
begin
c[i][j]:
=c[i][j]+z;
j:
=j+lowbit(j);
end;
i:
=i+lowbit(i);
end;
end;
function gs(x,y:longint):longint;
var ans,i,j:longint;
begin
ans:
=0;
i:
=x;
while i>0 do
begin
j:
=y;
while j>0 do
begin
ans:
=ans+c[i][j];
j:
=j-lowbit(j);
end;
i:
=i-lowbit(i);
end;
gs:
=ans;
end;
begin
assign(input,
'matrix.in'); reset(input);
assign(output,
'matrix.out'); rewrite(output);
readln(z);
while z<>0 do
begin
dec(z);
readln(n,m);
for i:=1 to m do
begin
read(ch);
case ch of
'C':
begin
readln(x1,y1,x2,y2);
chg(x1,y1,
1);
chg(x1,y2
+1,-1);
chg(x2
+1,y1,-1);
chg(x2
+1,y2+1,1);
end;
'Q':
begin
readln(x1,y1);
writeln(gs(x1,y1)
and 1);
end;
end;
end;
writeln;
fillchar(c,sizeof(c),
0);
end;
close(input); close(output);
end.

Cube

 

 

const maxn=100;
var i,n,m,ch,x1,x2,y1,y2,z1,z2:longint;
c:
array[1..maxn,1..maxn,1..maxn]of longint;
function lowbit(x:longint):longint;
begin
lowbit:
=x and -x;
end;
procedure chg(x,y,z,ch:longint);
var i,j,k:longint;
begin
i:
=x;
while i<=n do
begin
j:
=y;
while j<=n do
begin
k:
=z;
while k<=n do
begin
c[i][j][k]:
=c[i][j][k]+ch;
k:
=k+lowbit(k);
end;
j:
=j+lowbit(j);
end;
i:
=i+lowbit(i);
end;
end;
function gs(x,y,z:longint):longint;
var ans,i,j,k:longint;
begin
ans:
=0;
i:
=x;
while i>0 do
begin
j:
=y;
while j>0 do
begin
k:
=z;
while k>0 do
begin
ans:
=ans+c[i][j][k];
k:
=k-lowbit(k);
end;
j:
=j-lowbit(j);
end;
i:
=i-lowbit(i);
end;
gs:
=ans;
end;
begin
assign(input,
'cube.in'); reset(input);
assign(output,
'cube.out'); rewrite(output);
while not eof do
begin
readln(n,m);
for i:=1 to m do
begin
read(ch);
case ch of
0:
begin
readln(x1,y1,z1);
writeln(gs(x1,y1,z1)
and 1);
end;
1:
begin
readln(x1,y1,z1,x2,y2,z2);
chg(x1,y1,z1,
1);
chg(x1,y2
+1,z1,-1);
chg(x2
+1,y1,z1,-1);
chg(x2
+1,y2+1,z1,1);
chg(x1,y1,z2
+1,-1);
chg(x1,y2
+1,z2+1,1);
chg(x2
+1,y1,z2+1,1);
chg(x2
+1,y2+1,z2+1,-1);
end;
end;
end;
fillchar(c,sizeof(c),
0);
end;
close(input); close(output);
end.

 

BOB HAN 原创 转载请注明出处 http://www.cnblogs.com/Booble/

本文图片来自 http://hi.baidu.com/edelweisszf/blog/item/dd8a2fa2dfc60babcbefd013.html

(写的也很好 建议一起看)

posted on 2010-10-24 20:35  Master_Chivu  阅读(2268)  评论(1编辑  收藏  举报

导航