BZOJ 3155: Preprefix sum 树状数组题解

Time Limit: 1 Sec Memory Limit: 512 MB
Submit: 1639 Solved: 728

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

Source

Katharon+#1


这题看起来有点奇怪,貌似需要维护的东西不能直接求的,但是其实我们可以间接地求,以后要学会这种转化的思路,拿着式子多多推理一下,而不是直接去看题解,说不定就捣鼓出来了呢


S表示前缀和,则
S=a1,a1+a2,a1+a2+a3....
SS表示前缀和的前缀和
SS=a1,2a1+a2,3a1+2a2+a1...
则把每个S的值全部乘以i之后,令新的数组为S’
S=a1,2a1+2a2,3a1+3a2+3a3...
用S’减去SS会发现一个很神奇的效果,令新数组为SS’
SS=0a1,0a1+1a2,0a1+1a2+2a3...
我们发现又是升序的有规律的有前缀和的性质的东西了,所以我们只需要维护SS’数组和原来的数组S,最后减回去就是SS数组了


#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int n,m;
const int MAXN = 100000 + 100;
long long a[MAXN], C1[MAXN], C2[MAXN];
long long query_fir( int x ) { long long tmp = 0; while( x ) { tmp += C1[x]; x -= x & (-x); } return tmp; }
long long query_sec( int x ) { long long tmp = 0; while( x ) { tmp += C2[x]; x -= x & (-x); } return tmp; }
void modify_fir( int x, long long num ) { while( x <= n ) { C1[x] += num; x += x & (-x); } }
void modify_sec( int x, long long num ) { while( x <= n ) { C2[x] += num; x += x & (-x); } }
int main( ) {
    scanf( "%d%d", &n, &m );
    for( register int i = 1; i <= n; i++ ) {
        scanf( "%lld", &a[i]);
        modify_fir( i, a[i] );
        modify_sec( i, ( i - 1 ) * a[i] );
    }
    for( register int i = 1; i <= m; i++ ) { char ch[10]; int x;
        scanf( "%s", ch );
        if( ch[0] == 'Q' ) {
            scanf( "%d", &x );
            printf( "%lld\n", x * query_fir( x ) - query_sec( x ) );
        } else {
            long long y;
            scanf( "%d%lld", &x, &y );
            long long tmp = y - a[x];
            a[x] += tmp;
            modify_fir( x, tmp );
            modify_sec( x, ( x - 1 ) * tmp );
        }
    }
    return 0;
}

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值