题目: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; }