题目描述
如题,已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数数加上x
2.求出某一个数的和
输入输出格式
输入格式:第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。
第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。
接下来M行每行包含2或4个整数,表示一个操作,具体如下:
操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k
操作2: 格式:2 x 含义:输出第x个数的值
输出格式:输出包含若干行整数,即为所有操作2的结果。
输入输出样例
输入样例#1:
5 5 1 5 4 2 3 1 2 4 2 2 3 1 1 5 -1 1 3 5 7 2 4
输出样例#1:
6 10
说明
时空限制:1000ms,128M
数据规模:
对于30%的数据:N<=8,M<=10
对于70%的数据:N<=10000,M<=10000
对于100%的数据:N<=500000,M<=500000
样例说明:
故输出结果为6、10
1 #include<iostream> 2 using namespace std; 3 4 const int N = 500100 ; 5 6 int n,m,a; 7 int ch,x,y,v; 8 int sum[N];//树状数组 9 10 int lowbit(int x) 11 { 12 return x&(-x); 13 } 14 15 void update(int p,int v) //将第P个数增加v 16 { 17 while(p<=n) 18 { 19 sum[p] += v; 20 p += lowbit(p); 21 } 22 } 23 24 int query(int p) //查询第p个点的值是多少 25 { 26 int ans=0; 27 while(p) 28 { 29 ans += sum[p]; 30 p -= lowbit(p); 31 } 32 return ans; 33 } 34 35 int main() 36 { 37 ios::sync_with_stdio(false) ; 38 cin>>n>>m; 39 int last=0; 40 for (int i=1;i<=n;i++) 41 { 42 cin>>a; 43 update(i,a-last); //建树 44 last = a; 45 /* 46 这里运用了差分思想,假设原本的数据存在a数组中, 47 那么c数组储存的就是c[i]=a[i]-a[i-1],如果c[1]=a[1],那么很明显 48 a[i]=c[i]+c[i-1]+c[i-2]+...+c[2]+c[1]. 49 这样我们每次单点查询的时候只要加上c数组的前缀就可以了。 50 */ 51 } 52 for (int i=1;i<=m;++i) 53 { 54 cin>>ch; 55 if (ch==1) //区间修改 56 { 57 cin>>x>>y>>v; 58 update(x,v); 59 update(y+1,-v); 60 } 61 if (ch==2) //单点查询 62 { 63 cin>>x; 64 cout<<query(x)<<endl; 65 } 66 } 67 return 0; 68 }
更新模板
1 #include<cstdio> 2 int sum[500100]; 3 int n,m,last = 0; 4 5 void update(int p,int v) { 6 for (; p<=n; p+=p&(-p)) sum[p] += v; 7 } 8 int query(int p) { 9 int ans = 0; 10 for (; p; p-=p&(-p)) ans += sum[p]; 11 return ans; 12 } 13 14 int main() 15 { 16 scanf("%d%d",&n,&m); 17 for (int a,i=1; i<=n; ++i) 18 { 19 scanf("%d",&a); 20 update(i,a-last); 21 last = a; 22 } 23 for (int x,y,z,a,i=1; i<=m; ++i) 24 { 25 scanf("%d",&a); 26 if (a==1) //区间修改 27 { 28 scanf("%d%d%d",&x,&y,&z); 29 update(x,z); 30 update(y+1,-z); 31 } 32 else //单点查询 33 { 34 scanf("%d",&x); 35 printf("%d ",query(x)); 36 } 37 } 38 return 0; 39 }