Description
Input
Output
Sample Input
1 10 1 2 3 4 5 6 7 8 9 10 Query 1 3 Add 3 6 Query 2 7 Sub 10 2 Add 6 3 Query 3 10 End
Sample Output
Case 1: 6 33 59
用了还几个小时,终于把线段树看懂了,多做几次练习练习,代码中有详细的注释,下面对题目中给的样例做分析
#include<stdio.h> #include<string.h> struct seg { int l;//区间开端 int r;//区间结束 int n;//权值 } T[150011]; void build(int l,int r,int k) //构造线段树,此时权值全为0 { int mid; if(l==r)//如果是叶子节点,权值为0 { T[k].l=l;//区间左端点为l T[k].r=r;//区间右端点为r T[k].n=0;//权值为0 return ; } mid=(l+r)/2; T[k].l=l; T[k].r=r; T[k].n=0; build(l,mid,2*k);//二分分解区间,2*k为左孩子 build(mid+1,r,2*k+1);//2*k+1为右孩子 } void insert(int n,int d,int k)//增加线段树 { int mid; if(T[k].l==T[k].r&&T[k].l==d)//寻找符合条件的叶子节点 { T[k].n+=n; //权值加n return ; } mid=(T[k].l+T[k].r)>>1; // 就是除以2,刚进来时为根节点 if(d<=mid) insert(n,d,2*k);//如果结点在左边,就对左边继续二分查找 else insert(n,d,2*k+1);//如果结点在右边,就对右边继续二分查找 T[k].n=T[2*k].n+T[2*k+1].n;//双亲节点的权值为孩子节点之和 } int ans; void search(int l,int r,int k)//递归寻找权值 { int mid; if(T[k].l==l&&T[k].r==r)//如果线段树的左右端点是要找的 { ans+=T[k].n; //加上权值 return ; } mid=(T[k].l+T[k].r)>>1;//就是对2取商 if(r<=mid) search(l,r,2*k);//如果在区间右边,向左寻找 else if(l>mid) search(l,r,2*k+1);//如果在区间右边,向左寻找 else { search(l,mid,2*k); //如果在区间内,就继续二分查找 search(mid+1,r,2*k+1); } } int main() { int j,t,n,i,temp,m,a,b; char str[11]; scanf("%d",&t); //一共t组数据 for(j=1; j<=t; j++) { scanf("%d",&n);//输入区间的数量 build(1,n,1); //构造线段树 for(i=1; i<=n; i++) { scanf("%d",&temp); insert(temp,i,1);//从根结点进入,添加数据 } printf("Case %d:\n",j); while(scanf("%s",str),strcmp(str,"End"))//输入命令,当End时结束 { scanf("%d%d",&a,&b);//输入变化的区间和值 if(strcmp(str,"Add")==0) //如果是增加 insert(b,a,1); //从根节点进入,添加数据 else if(strcmp(str,"Sub")==0)//减少 insert(-b,a,1); //从根节点进入,添加数据 else { ans=0; search(a,b,1);//从根节点进入,查找计算总和 printf("%d\n",ans); } } } return 0; }