[bzoj 3155] Preprefix sum

[bzoj 3155] Preprefix sum

Description

Input

第一行给出两个整数N,M。分别表示序列长度和操作个数
接下来一行有N个数,即给定的序列a1,a2,….an
接下来M行,每行对应一个操作,格式见题目描述

Output

对于每个询问操作,输出一行,表示所询问的SSi的值。

Sample Input

5 3 
1 2 3 4 5 
Query 5 
Modify 3 2 
Query 5 

Sample Output

35 
32 

HINT

1<=N,M<=100000,且在任意时刻0<=Ai<=100000

唉,这题可以维护系数做,也可以直接用线段数搞.这里我用简单的方法,直接搞!我们直接维护sum[i]的值,那么询问pos相当于区间\([1,pos]\)的sum和.修改A[i]也好办,区间修改i~n的sum值就行了,改成加法的操作,就可以上线段树.不过也可以用拆一下SSi来用树状数组维护一下系数.有点麻烦,我不讲了.但是这里我用树状数组代替了线段树执行区间修改和区间查询的操作,至于这个是怎么来的,其实也有点像之前那个维护系数的方法,用差分的思路去做就好了.

#include <cstdio>
#include <cstring>
#include <algorithm>

typedef long long LL;

static const int maxm = 1e5 + 10;

LL tr1[maxm],tr2[maxm],A[maxm],sum[maxm];
char ch[10];
int n,m,pos;

int lowbit(int x){
	return x&-x;
}

void Upt(int k,LL val){
	for(int i = k; i <= n ;i += lowbit(i))
		tr1[i] += val,tr2[i] += 1LL * k * val;
}

LL Query(int k){
	LL ret = 0;
	for(int i = k ; i ;i -= lowbit(i))
		ret += 1LL * (k+1) * tr1[i] - tr2[i];
	return ret;
}

int main(){
	LL x;
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n ;i++)
		scanf("%lld",&A[i]),sum[i]=sum[i-1]+A[i];
	
	for(int i = 1;i <= n ;i++)
		Upt(i,sum[i]),Upt(i+1,-sum[i]);
	
	while(m--){
		scanf("%s",ch);	
		if(ch[0]=='M'){
			scanf("%d%lld",&pos,&x);
			Upt(pos,x-A[pos]);Upt(n+1,A[pos]-x);
			A[pos]=x;
		}else{
			scanf("%d",&pos);
			printf("%lld\n",Query(pos));
		}
	}

	return 0;
}

传送门

posted @ 2017-05-17 21:45  Exbilar  阅读(164)  评论(0编辑  收藏  举报