Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u
Description
Lily 特别喜欢养花,但是由于她的花特别多,所以照料这些花就变得不太容易。她把她的花依次排成一行,每盆花都有一个美观值。如果Lily把某盆花照料的好的话,这盆花的美观值就会上升,如果照料的不好的话,这盆花的美观值就会下降。有时,Lily想知道某段连续的花的美观值之和是多少,但是,Lily的算术不是很好,你能快速地告诉她结果吗?
Input
第一行一个整数T,表示有T组测试数据。
每组测试数据的第一行为一个正整数N(N<=50000),表示Lily有N盆花。接下来有N个正整数,第i个正整数ai表示第i盆花的初始美观值(1<=ai<=50)。
接下来每行有一条命令,命令有4种形式:
(1)Add i j, i和j为正整数,表示第i盆花被照料的好,美观值增加j(j<=30)
(2)Sub i j, i和j为正整数,表示第i盆花被照料的不好,美观值减少j(j<=30)
(3)Query i j, i和j为正整数,i<=j,表示询问第i盆花到第j盆花的美观值之和
(4)End,表示结束,这条命令在每组数据最后出现
每组数据的命令不超过40000条
Output
对于第i组数据,首先输出"Case i:"和回车。
对于每个"Query i j"命令,输出第i盆花到第j盆花的美观值之和。
Sample Input
19
7 9 8 4 4 5 4 2 7
Query 7 9
Add 4 9
Query 3 6
Sub 9 6
Sub 3 3
Query 1 9
End
7 9 8 4 4 5 4 2 7
Query 7 9
Add 4 9
Query 3 6
Sub 9 6
Sub 3 3
Query 1 9
End
Sample Output
Case 1:13
30
50
View Code
30
50
//运用线段树知识:对于查找线段树,有如下知识:
对于每一条线段A,从根节点开始遍历这棵线段树,对于每一个当前遍历的结点NODE(其实线段树中每一个结点就是一条线段),考虑四种情况:
a)如果线段A包含在线段NODE的左半区间,那么从NODE的左儿子(其实就是NODE的左半区间)开始遍历这棵树
b)如果线段A包含在线段NODE的右半区间,那么从NODE的右儿子(其实就是NODE的右半区间)开始遍历这棵树
c)如果线段A刚好和线段NODE重合,停止遍历,并将NODE中的count字段加1
d)除了以上的情况,就将线段A的左半部分在NODE的左儿子处遍历,将A的右半部分在NODE的右儿子处遍历
源代码如下:
#include <iostream> #include <stdio.h> #include <cstring> using namespace std; struct node { int left,right; int value; }tree[140000]; int r[50010],sum;//r[50010]是存放每个点上的人数,sum是用来存放查询的结果。 void make (int x,int y,int num) { tree[num].left=x; tree[num].right=y; if(tree[num].left==tree[num].right) tree[num].value=r[y];//如果x==y,说明已经是叶子节点了,没有儿子节点了,就显现成熟单个营地,人数就是r[y] else { make(x,(x+y)/2,num+num);//构造左儿子树 make((x+y)/2+1,y,num+num+1);//构造右儿子树 tree[num].value=tree[num+num+1].value+tree[num+num].value;//父节点的人数等于子结点人数之和,线段被分成两段。 } } void add (int x,int y,int num)//从根节点不断往下更改,只要包含该点x的线段子都增加相应的数量y { tree[num].value+=y; if(tree[num].left==x&&tree[num].right==x) return;//找到x的叶子节点。停止。 else if(x>(tree[num].left+tree[num].right)/2) add(x,y,num+num+1);//点x在该线段的右边,查询右子节点。 else add(x,y,num+num);//否则查询左子节点 } void sub(int x,int y,int num) { tree[num].value-=y; if(tree[num].left==x&&tree[num].right==x) return; else if(x>(tree[num].left+tree[num].right)/2) sub(x,y,num+num+1); else sub(x,y,num+num); } void query(int x,int y,int num) { if(x<=tree[num].left&&y>=tree[num].right)//找到要求的线段区间,返回其值 sum+=tree[num].value; else { int mid =(tree[num].left+tree[num].right)/2; if(x>mid) query(x,y,num+num+1);//要查询的线段在该线段右边,查询该线段的右子节点 else if(y<=mid) query(x,y,num+num);//要查询的线段在该线段左边,查询该线段的左子节点 else { query(x,y,num+num);//要查询的线段在该线段中间,分段查询,左右节点都查。 query(x,y,num+num+1); } } } int main() { int a,b,t,n; char command[6]; scanf("%d",&t); int j=0; while(t--) { memset(r,0,sizeof(r)); scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&r[i]); make(1,n,1); printf("Case %d: ",++j); while (cin>>command) { if(strcmp(command,"End")==0) break; if(strcmp(command,"Query")==0) { scanf("%d %d",&a,&b); sum=0; query(a,b,1); printf("%d ",sum); } if(strcmp(command,"Add")==0) { scanf("%d %d",&a,&b); add(a,b,1); } if(strcmp(command,"Sub")==0) { scanf("%d %d",&a,&b); sub(a,b,1); } } } return 0; }