POJ 3321 Apple Tree
http://acm.pku.edu.cn/JudgeOnline/problem?id=3321
POJ 2481 Cows
http://acm.pku.edu.cn/JudgeOnline/problem?id=2481
POJ 2155 Matrix
http://acm.pku.edu.cn/JudgeOnline/problem?id=2155
今天在POJ淘了这几道题目,学习了一下树状数组的用法,跟大家分享一下心得。可以把树状数组看成一种数据结构,对于一个数组,如果有多次操作,每次的操作有两种:1、修改数组中某一元素的值,2、求和,求数组元素a[1]+a[2]+…a[num]的和。这是树状数组最基本的应用了,用树状数组可以实现O(log(n))的修改以及O(log(n))的求和。当然用线段树完全可以胜任这些计算,但是线段树写起来代码比较长,并且线段树要占用2*n大小的空间。下面先给出树状数组的三个基本操作的函数:
int lowbit(int k)
{
return k&(-k);
}
//lowbit函数是计算k的二进制位最低位不为0的数字的权值。
void Modify(int num, int v)
{
while(num <= n)
{
c[num]+=v;
num+=lowbit(num);
}
}
//Modify函数是往数组c中修改值,更新整个数组的值,实现了操作1;
int Sum(int num)
{
int ans=0;
while(num > 0)
{
ans+=c[num];
num-=lowbit(num);
}
return ans;
}
//Sum函数返回数组元素a[1]+a[2]+…a[num]的和,实现操作2;
树状数组的巧妙之处在于对于数组下标的二进制的操作,假设a[1...N]为原数组,定义c[1...N]为对应的树状数组:
c[i] = a[i - 2^k + 1] + a[i - 2^k + 2] + ... + a[i]
其中k为i的二进制表示末尾0的个数,所以2^k即为i的二进制表示的最后一个1的权值.
可以把树状数组看作是把数组分成了若干个2^k大小的空间。对于一个下标i,c[i]的值是由i/(lowbit(i))个数组元素的值所组成的,每次步进的单位是k=lowbit(i),这个有点像二分归并的思想!这样就可以实现O(log(n))的修改和查询。
下面是树状数组的具体应用:
3321 Apple Tree 一棵树上长了苹果,每一个树枝节点上有长苹果和不长苹果两种状态,两种操作,一种操作能够改变树枝上苹果的状态,另一种操作询问某一树枝节点一下的所有的苹果有多少。具体做法是做一次dfs,记下每个节点的开始时间low[i]和结束时间high[i],那么对于i节点的所有子孙的开始时间和结束时间都应位于low[i]和high[i]之间,另外用一个数组c[i]记录附加在节点i上的苹果的个数,然后用树状数组统计low[i]到high[i]之间的附加苹果总数。这里用树状数组统计区间可以用Sum(high[i])-Sum(low[i]-1)来计算。
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
1
#include <stdio.h>
2
#include <string.h>
3
#include <vector>
4
using namespace std;
5![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
6
//vector<int> g[100005];
7
struct Node
8![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
9
int v;
10
struct Node *next;
11
}g[100005];
12
int n,m,cnt,low[100005],high[100005],c[100005],flag[100005];
13
bool mark[100005];
14![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
15
void dfs(int v)
16![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
17
struct Node *p=g[v].next;
18
mark[v]=true;
19
cnt++;
20
low[v]=cnt;
21
while(p)
22![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
23
if(!mark[p->v])
24
dfs(p->v);
25
p=p->next;
26
}
27
high[v]=cnt;
28
}
29
int lowbit(int k)
30![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
31
return k&(-k);
32
}
33
void Modify(int num, int v)
34![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
35
while(num <= n)
36![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
37
c[num]+=v;
38
num+=lowbit(num);
39
}
40
}
41
int Sum(int num)
42![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
43
int ans=0;
44
while(num > 0)
45![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
46
ans+=c[num];
47
num-=lowbit(num);
48
}
49
return ans;
50
}
51![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
52
int main()
53![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
54
int i,j,a,b,ans;
55
char temp[10];
56
struct Node *p;
57
//freopen("in.txt","r",stdin);
58
scanf("%d",&n);
59
memset(g,0,sizeof(g));
60
for(i=1; i<n; i++)
61![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
62
scanf("%d%d",&a,&b);
63
p=new Node;
64
p->next=g[a].next;
65
p->v=b;
66
g[a].next=p;
67
p=new Node;
68
p->next=g[b].next;
69
p->v=a;
70
g[b].next=p;
71
}
72
memset(mark,false,sizeof(mark));
73
memset(c,0,sizeof(c));
74
for(i=1; i<=n; i++)
75
flag[i]=1;
76
cnt=0;
77
dfs(1);
78
scanf("%d",&m);
79
while(m--)
80![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
81
scanf("%s",temp);
82
if(temp[0] == 'Q')
83![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
84
scanf("%d",&a);
85
ans=high[a]-low[a]+1+Sum(high[a])-Sum(low[a]-1);
86
printf("%d\n",ans);
87
}
88
else
89![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
90
scanf("%d",&a);
91
if(flag[a]) Modify(low[a],-1);
92
else Modify(low[a],1);
93
flag[a]^=1;
94
}
95
}
96
return 0;
97
}
98![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
99![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
2481 Cows 给n个区间[Si,Ei],区间[Sj,Ej]< [Si,Ei] 有 Si <= Sj and Ej <= Ei and Ei - Si > Ej – Sj。按y坐标从小到达,x坐标从大到小的顺序排序,然后从后往前扫描,记录i之前所有的j区间Sj<Si的个数,这个用树状数组实现。扫描一遍可得出结果。
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
1
#include <stdio.h>
2
#include <string.h>
3
#include <algorithm>
4
using namespace std;
5![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
6
struct P
7![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
8
int x,y,id;
9
}p[100005];
10
int n,a[100005],max_n,b[100005];
11![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
12
int lowbit(int k)
13![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
14
return k&(-k);
15
}
16
void Modify(int num, int v)
17![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
18
while(num <= max_n)
19![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
20
a[num]+=v;
21
num+=lowbit(num);
22
}
23
}
24
int Sum(int num)
25![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
26
int ans=0;
27
if(num <= 0) return 0;
28
while(num)
29![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
30
ans+=a[num];
31
num-=lowbit(num);
32
}
33
return ans;
34
}
35
bool operator <(const P a, const P b)
36![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
37
if(a.y == b.y) return a.x > b.x;
38
return a.y < b.y;
39
}
40![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
41
int main()
42![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
43
int i;
44
//freopen("in.txt","r",stdin);
45
while(scanf("%d",&n), n)
46![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
47
max_n=0;
48
for(i=0; i<n; i++)
49![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
50
scanf("%d%d",&p[i].x,&p[i].y);
51
p[i].id=i;
52
p[i].x++;
53
p[i].y++;
54
if(p[i].y > max_n) max_n=p[i].y;
55
}
56
sort(p,p+n);
57
memset(a,0,sizeof(a));
58
for(i=n-1; i>=0; i--)
59![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
60
if(i != n-1 && p[i].y == p[i+1].y && p[i].x == p[i+1].x)
61
b[p[i].id]=b[p[i+1].id];
62
else
63
b[p[i].id]=Sum(p[i].x);
64
Modify(p[i].x,1);
65
}
66
for(i=0; i<n; i++)
67![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
68
if(i) printf(" ");
69
printf("%d",b[i]);
70
}
71
printf("\n");
72
}
73
return 0;
74
}
75![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
76![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
2155 Matrix 有n*n的0,1矩阵,两种操作,1、翻转矩形(x1,y1)(x2,y2)的值,2、输出位置为(x,y)矩阵处的值。先考虑一维的情况,设A<B,那么要翻转[A,B]之间的值,可以分解为两步操作,先翻转[1,A-1],然后再翻转[1,B],其中翻转的次数就可以用树状数组来计算。然后再将次操作扩展到二维的情形,只需将x方向与y方向套成一个二重循环即可。从这里我们也可以看到树状数组处理类似问题时比线段树的优越性。从代码的长度,空间消耗上面,树状数组都有明显的优势。
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
1
#include <stdio.h>
2
#include <string.h>
3![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
4
int a[1005][1005],n,m;
5![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
6
int lowbit(int k)
7![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
8
return k&(-k);
9
}
10
void Modify(int x1, int y1, int x2, int y2)
11![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
12
int i,j;
13
for(i=x1-1; i>0; i-=lowbit(i))
14![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
15
for(j=y1-1; j>0; j-=lowbit(j))
16![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
17
a[i][j]^=1;
18
}
19
for(j=y2; j>0; j-=lowbit(j))
20![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
21
a[i][j]^=1;
22
}
23
}
24
for(i=x2; i>0; i-=lowbit(i))
25![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
26
for(j=y1-1; j>0; j-=lowbit(j))
27![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
28
a[i][j]^=1;
29
}
30
for(j=y2; j>0; j-=lowbit(j))
31![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
32
a[i][j]^=1;
33
}
34
}
35
}
36
int Sum(int x, int y)
37![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
38
int ans=0,i,j;
39
for(i=x; i<=n; i+=lowbit(i))
40![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
41
for(j=y; j<=n; j+=lowbit(j))
42
ans^=a[i][j];
43
}
44
return ans;
45
}
46![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
47
int main()
48![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
49
int i,j,x1,x2,y1,y2,cases,ic=0;
50
char temp[10];
51
//freopen("in.txt","r",stdin);
52
scanf("%d",&cases);
53
while(cases--)
54![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
55
if(ic++) printf("\n");
56
scanf("%d%d",&n,&m);
57
memset(a,0,sizeof(a));
58
while(m--)
59![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
60
scanf("%s",temp);
61
if(temp[0] == 'C')
62![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
63
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
64
Modify(x1,y1,x2,y2);
65
}
66
else
67![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
68
scanf("%d%d",&x1,&y1);
69
printf("%d\n",Sum(x1,y1));
70
}
71
}
72
}
73
return 0;
74
}
75![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
76![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
posted on 2008-09-24 16:35
飞飞 阅读(3408)
评论(2) 编辑 收藏 引用 所属分类:
ACM/ICPC