洛谷P1438 无聊的数列 (线段树+差分)

变了个花样,在l~r区间加上一个等差数列,等差数列的显著特点就是公差d,我们容易想到用线段树维护差分数组,在l位置加上k,在l+1~r位置加上d,最后在r+1位置减去k+(l-r)*d,这样就是在差分数组上操作,利用线段树打标记容易实现。

最后对于每个查询的位置t,查询1~t的区间和就是t位置上的数值。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
#define ll long long
int data[N];
struct node{
    int l,r;
    ll sum,tag;
}t[N<<2];

void pushup(int k){
    t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
}

void build(int k,int l,int r){
    t[k].r=r,t[k].l=l;
    if(l==r) {
        t[k].sum=data[t[k].l];
        return ;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);build(k<<1|1,mid+1,r);
    pushup(k);
}
void rev(int k,int x){
    t[k].tag+=x;
    t[k].sum+=(t[k].r-t[k].l+1)*x;
}

void pushdown(int k){
    rev(k<<1,t[k].tag);rev(k<<1|1,t[k].tag);
    t[k].tag=0;
}

void change(int k,int l,int r,ll x){
    if(t[k].l>=l && t[k].r<=r){
        t[k].tag+=x,t[k].sum+=(t[k].r-t[k].l+1)*x;
        return ;
    }
    int mid=(t[k].l+t[k].r)>>1;
    pushdown(k);
    if(l<=mid) change(k<<1,l,r,x);
    if(r>mid) change(k<<1|1,l,r,x);
    pushup(k);
}

ll query(int k,int l,int r){
    if(t[k].l>=l && t[k].r<=r) return t[k].sum;
    int mid=(t[k].l+t[k].r)>>1;
    pushdown(k);
    ll ans=0;
    if(l<=mid) ans+=query(k<<1,l,r);
    if(r>mid) ans+=query(k<<1|1,l,r);
    return ans; 
}

int main(){
    int n,m,opt,l,r,k,d,t;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&data[i]);
    for(int i=n;i>1;i--) data[i]=data[i]-data[i-1];//差分数组 
    build(1,1,n); 
    while(m--){
        scanf("%d",&opt);
        if(opt==1){
            scanf("%d%d%d%d",&l,&r,&k,&d);
            change(1,l,l,k);
            if(l+1<=r) change(1,l+1,r,d);
            if(r+1<=n) change(1,r+1,r+1,-(k+(r-l)*d));//注意加判断 
        }
        else{
            scanf("%d",&t);
            cout<<query(1,1,t)<<endl;
        } 
    }
}

 

posted @ 2022-06-02 20:11  YHXo  阅读(50)  评论(0)    收藏  举报