题目:http://acm.hdu.edu.cn/showproblem.php?pid=1166
基维百科:http://zh.wikipedia.org/wiki/%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84
http://duanple.blog.163.com/blog/static/7097176720081131113145832/
树状数组源码及部分注释:
#include<stdio.h>
#include<string.h>
#define MAX 50010
int N;
int per[MAX],C[MAX];
int lowbit(int n)
{//此函数实际就是将n的二进制表示形式留下最右边的1,其他位都变为0
return n&(-n);
}
void update(int i,int x)//更新值
{
while(i <= N)
{
C[i] += x;
i += lowbit(i);
}
}
int check(int n)//求从第一个数到第n个数的和
{
int sum = 0;
while( n > 0)
{
sum += C[n];
n -= lowbit(n);
}
return sum;
}
int main()
{
int i,T,k,j,num = 1,a,b;
char str[10];
scanf("%d",&T);
while( T-- )
{
scanf("%d",&N);
//初始化数组C
memset(C,0,sizeof(C));
//输入原始数据
for(i = 1; i <= N; i++)
scanf("%d",&per[i]);
//构造树状数组
for(i = 1; i <= N; i++)
{
j = lowbit(i);
for(k = i - j + 1; k <= i; k++)
C[i] += per[k];
}
printf("Case %d:\n",num++);
while(scanf("%s",str) && str[0] != 'E')
{
scanf("%d%d",&a,&b);
switch( str[0] )
{
case 'Q':
printf("%d\n",check(b) - check(a-1));
break;
case 'S':
update(a,-b);
break;
case 'A':
update(a,b);
break;
}
}
}
return 0;
}
线段树(Segment Tree)是一种二叉搜索树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树。叶节点数目为N,即整个线段区间的长度。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。
线段树求解源代码及部分注释:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define maxn 50010
struct node
{
int left,right;
int count;
}st[maxn * 6];
void build(int curnode,int left,int right)//创建线段树,
{
st[curnode].left = left;
st[curnode].right = right;
st[curnode].count = 0;
if(left == right)//当到达叶子节点的时候则返回
return ;
//将子树继续二分,
build(curnode*2,left,(left+right)/2);
build(curnode*2+1,(left+right)/2+1,right);
}
void update(int curnode,int seq,int cnt)
{//更新线段树
if(seq <= st[curnode].right && seq >= st[curnode].left)
{
st[curnode].count += cnt;
update(curnode*2,seq,cnt);
update(curnode*2+1,seq,cnt);
}
}
int total;
void sum(int curnode,int left,int right)
{//求和
int mid = (st[curnode].left + st[curnode].right)/2;
if(st[curnode].left == left && st[curnode].right == right)
total += st[curnode].count;
else
if(right <= mid )
sum(curnode * 2,left,right);
else
if(left > mid)
sum(curnode*2+1,left,right);
else
{
sum(curnode*2,left,mid);
sum(curnode*2+1,mid+1,right);
}
}
int main()
{
int T,num,pernum,i;
char ins[10];
int a,b,c = 1,flg;
scanf("%d",&T);
while( T--)
{
scanf("%d",&num);
memset(st,0,sizeof(st));
build(1,1,num);
for( i = 1; i <= num; i++)
{
scanf("%d",&pernum);
update(1,i,pernum);
}
printf("Case %d:\n", c++);
flg = 1;
while( scanf("%s",ins) != EOF && ins[0] != 'E')
{
scanf("%d%d",&a,&b);
switch(ins[0])
{
case 'Q':
total = 0;
sum(1,a,b);
printf("%d\n",total);
break;
case 'A':
update(1,a,b);
break;
case 'S':
update(1,a,-b);
break;
}
}
}
return 0;
}