2018-09-09 JSK-31460丨CCPC徐州站网赛丨线段树区间和丨我死了

题意:
n的数组,q次操作
每次op x y
op==1 询问[x,y],求算 a[l]×L+a[l+1]×(L1)++a[r1]×2+a[r] a [ l ] × L + a [ l + 1 ] × ( L − 1 ) + ⋯ + a [ r − 1 ] × 2 + a [ r ]
op==2 修改a[x]成y

思路:
看上去是个线段树,仔细一想还真是个线段树。但是线段树维护的是a的前缀和。对于询问, 前缀和的区间和,减去(L-1)前缀和,就可得到题目要求的答案。对于修改,每次修改了某一位,那么这一位到末尾的每一位前缀和都要修改,修改的数值是y-a[x] (也就是一个变化量)
想清楚之后,套一个区间修改的模板就可以过啦。
然后我又被自己以前代码坑死了,这次是因为没有在pushdown的时候给区间新值乘上长度(因为是从最值线段树改过来的x)
所以我对线段树的了解程度还不够透彻,白白贡献了两次罚时,气死咯。

さらに向こうへ Puls Ultra!!


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100005;
ll a[N],b[N],tree[N<<2],add[N<<2],L,R;
int n,q;
void build(int root,int l,int r){
    add[root]=0;
    if(l==r){
        tree[root]=a[l];
    } else {
        int m=l+r>>1;
        build(root<<1,l,m);
        build(root<<1|1,m+1,r);
        tree[root]=tree[root<<1]+tree[root<<1|1];
    }
}

void PushDown(int root,int l,int r){
    int m=(l+r)/2;
    tree[root<<1]+=add[root]*(m-l+1);
     add[root<<1]+=add[root];
    tree[root<<1|1]+=add[root]*(r-(m+1)+1);
     add[root<<1|1]+=add[root];
    add[root]=0;
}

ll query(int root,int l,int r)
{
    if(r<L||R<l)return 0;
    else if(L<=l&&r<=R)return tree[root];

    PushDown(root,l,r);
    int m=l+r>>1;
    return query(root<<1,l,m)+query(root<<1|1,m+1,r);
}

void update(int root,int l,int r,ll v)
{
    if(r<L||R<l)return ;
    else if(L<=l&&r<=R){
        add[root]+=v;
        tree[root]+=v*(r-l+1);
        return ;
    }
    PushDown(root,l,r);
    int m=l+r>>1;
    update(root<<1,l,m,v);
    update(root<<1|1,m+1,r,v);
    tree[root]=tree[root<<1]+tree[root<<1|1];
}

int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++){
        scanf("%lld",b+i);
        a[i]=a[i-1]+b[i];
    }
    build(1,1,n);
    while(q--){
        int op,x,y;
        scanf("%d%d%d",&op,&x,&y);
        if(op==1){

            L=x,R=y;
            ll ans=query(1,1,n);

            if(x!=1){
                L=x-1,R=x-1;
                ans-=(y-x+1)*query(1,1,n);
            }
            printf("%lld\n",ans);

        }else{
            L=x,R=n;
            update(1,1,n,y-b[x]);
            b[x]=y;
        }
    }
    return 0;
}
/*
5 100
1 2 3 4 5
2 1 0
1 1 5
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值