*情报局要研究敌人究竟演习什么战术,所以Tidy要随时向Derek汇报某一段连续的工兵营地一共有多少人,例如Derek问:“Tidy,马上汇报第3个营地到第10个营地共有多少人!”Tidy就要马上开始计算这一段的总人数并汇报。但敌兵营地的人数经常变动,而Derek每次询问的段都不一样,所以Tidy不得不每次都一个一个营地的去数,很快就精疲力尽了,Derek对Tidy的计算速度越来越不满:"你个死肥仔,算得这么慢,我炒你鱿鱼!”Tidy想:“你自己来算算看,这可真是一项累人的工作!我恨不得你炒我鱿鱼呢!”无奈之下,Tidy只好打电话向计算机专家Windbreaker求救,Windbreaker说:“死肥仔,叫你平时做多点acm题和看多点算法书,现在尝到苦果了吧!”Tidy说:"我知错了。。。"但Windbreaker已经挂掉电话了。Tidy很苦恼,这么算他真的会崩溃的,聪明的读者,你能写个程序帮他完成这项工作吗?不过如果你的程序效率不够高的话,Tidy还是会受到Derek的责骂的.
Input第一行一个整数T,表示有T组数据。
每组数据第一行一个正整数N(N<=50000),表示敌人有N个工兵营地,接下来有N个正整数,第i个正整数ai代表第i个工兵营地里开始时有ai个人(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询问,输出一个整数并回车,表示询问的段中的总人数,这个数保持在int以内。
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
思路:根据线段树思想套用单点修改和区间查询模板即可
代码:
1 #include <cstdio> 2 #include <fstream> 3 #include <algorithm> 4 #include <cmath> 5 #include <deque> 6 #include <vector> 7 #include <queue> 8 #include <string> 9 #include <cstring> 10 #include <map> 11 #include <stack> 12 #include <set> 13 #include <sstream> 14 #include <iostream> 15 #define mod 998244353 16 #define eps 1e-6 17 #define ll long long 18 #define INF 0x3f3f3f3f 19 using namespace std; 20 21 struct node 22 { 23 //l表示左边,r表示右边,sum表示该线段的值 24 int l,r,sum; 25 }; 26 node no[500000]; 27 //存放每个点的值 28 int number[50000]; 29 //存放每个点对应的节点 30 int pa[500000]; 31 //初始化 32 //k表示当前节点的编号,l表示当前区间的左边界,r表示当前区间的右边界 33 void build(int k,int l,int r) 34 { 35 no[k].l=l; 36 no[k].r=r; 37 //如果递归到最低点 38 if(l==r) 39 { 40 //赋值并记录该点对应的节点编号 41 no[k].sum=number[l]; 42 pa[l]=k; 43 return ; 44 } 45 //对半分 46 int mid=(l+r)/2; 47 //递归到左线段 48 build(k*2,l,mid); 49 //递归到右线段 50 build(k*2+1,mid+1,r); 51 //用左右线段的值更新该线段的值 52 no[k].sum=no[k*2].sum+no[k*2+1].sum; 53 } 54 //改边指定节点标号的值 55 //k表示当前节点的编号,x为正数表示加,x为负数表示减 56 void change(int k,int x) 57 { 58 no[k].sum +=x; 59 //如果该节点不是最高的节点则往上递归 60 if(k!=1) 61 { 62 change(k/2,x); 63 } 64 } 65 //查询指定区间内的所有的和 66 //k表示当前节点的编号,l表示当前区间的左边界,r表示当前区间的右边界 67 int query(int k,int l,int r) 68 { 69 //如果当前区间就是询问区间,完全重合,那么显然可以直接返回 70 if(no[k].l==l&&no[k].r==r) 71 { 72 return no[k].sum; 73 } 74 //取中值 75 int mid = (no[k].l+no[k].r)/2; 76 //如果询问区间包含在左子区间中 77 if(r<=mid) 78 { 79 return query(k*2,l,r); 80 } 81 else if(l>mid)//如果询问区间包含在右子区间中 82 { 83 return query(k*2+1,l,r); 84 } 85 else//如果询问区间跨越两个子区间 86 { 87 return query(k*2,l,mid)+query(k*2+1,mid+1,r); 88 } 89 } 90 91 int main() 92 { 93 //n表示样例数,a表示第几个样例 94 int n,a=1; 95 scanf("%d",&n); 96 while(n--) 97 { 98 //每个样例前最好把素有数据都初始化一边 99 memset(no,0,sizeof(no)); 100 memset(pa,0,sizeof(pa)); 101 memset(number,0,sizeof(number)); 102 int m; 103 scanf("%d",&m); 104 for(int i=1;i<=m;i++) 105 { 106 scanf("%d",&number[i]); 107 } 108 //初始化线段树 109 build(1,1,m); 110 printf("Case %d:\n",a++); 111 while(1) 112 { 113 string str; 114 int i,j; 115 cin>>str; 116 if(str[0]=='E') 117 { 118 break; 119 } 120 else 121 { 122 cin>>i>>j; 123 if(str[0]=='A') 124 { 125 change(pa[i],j); 126 } 127 else if(str[0]=='S') 128 { 129 change(pa[i],-1*j); 130 } 131 else if(str[0]=='Q') 132 { 133 printf("%d\n",query(1,i,j)); 134 } 135 } 136 } 137 } 138 }