BZOJ3155:Preprefix sum——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=3155

最朴素的想法是两棵树状数组,一个记录前缀和,一个记录前缀前缀和,但是第二个我们非常不好修改

但其实我们发现$SS_i=i*a1+(i-1)*a2+…+ai$,我们可以试图构造这样的“类等差”数列,这样我们就可以通过加加减减就能做了。

为了照顾最后一位的查询,我们就维护$(n-i+1)*ai$吧!

于是我们修改就变成了两个单点修改了,查询也就是很简单了,$qry(x,1)-qry(x,0)*(n-x)$(前一个是第二棵树状数组,后面的是第一棵)。

#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+5;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
int a[N],n,m;
ll tr[N][2];
inline int lowbit(int t){return t&-t;}
inline void add(int x,ll y,int on){
    for(int i=x;i<=n;i+=lowbit(i))tr[i][on]+=y;
}
inline ll qry(int x,int on){
    ll res=0;
    for(int i=x;i;i-=lowbit(i))res+=tr[i][on];
    return res;
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++){
    add(i,a[i]=read(),0);
    add(i,(ll)(n-i+1)*a[i],1);
    }
    while(m--){
    char ch[10];
    scanf("%s",ch);
    if(ch[0]=='Q'){
        int x=read();
        printf("%lld\n",qry(x,1)-qry(x,0)*(n-x));
    }else{
        int x=read(),y=read();
        add(x,y-a[x],0);
        add(x,(ll)(n-x+1)*(y-a[x]),1);
        a[x]=y;
    }
    }
    return 0;
}

 

posted @ 2018-06-23 10:00  luyouqi233  阅读(157)  评论(0编辑  收藏  举报