SDUT-4259 (2018年西安邀请赛K题)

题目链接:http://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Index/problemdetail/pid/4259.html

每年有两种操作,一是询问[L,R]内的土地这些年来的总产量,二是为[L,R]内的每一块地的年产量加1。

思路:假设X[i]表示一块土地第X[i]年增加年产量,则若询问年为y,总共更新了z次操作,那么这些年来的总产量为:

b[i]*y+\sum_{i=1}^{Z-1}i*(x[i+1]-x[i])+(y-x[z])*z 

若将y看为第z+1次更新操作的话,

b[i]*y+\sum_{i=1}^{Z}i*(x[i+1]-x[i])

将该式子展开化简即为:b[i]*y+y*z-\sum_{i=1}^{Z} x[i]

那么我们只需要用线段树维护两个东西,一个是区间加1操作,另一个是区间更新的总年份和。

而前面的b数组只需要一个前缀和就行。

#include
using namespace std;
typedef long long ll;

const int maxn=1e5+7;

ll lazy1[maxn<<2|1],lazy2[maxn<<2|1];
//年产量增量和,更新年份和;
ll sum1[maxn<<2|1],sum2[maxn<<2|1];

void pushdown(int l,int r,int k){
    int mid=(l+r)>>1;
    if(lazy1[k]){
        lazy1[k<<1]+=lazy1[k];
        lazy1[k<<1|1]+=lazy1[k];
        sum1[k<<1]+=lazy1[k]*(mid-l+1);
        sum1[k<<1|1]+=lazy1[k]*(r-mid);
        lazy1[k]=0;
    }

    if(lazy2[k]){
        lazy2[k<<1]+=lazy2[k];
        lazy2[k<<1|1]+=lazy2[k];
        sum2[k<<1]+=lazy2[k]*(mid-l+1);
        sum2[k<<1|1]+=lazy2[k]*(r-mid);
        lazy2[k]=0;
    }
}

void pushup(int k){
    sum1[k]=sum1[k<<1]+sum1[k<<1|1];
    sum2[k]=sum2[k<<1]+sum2[k<<1|1];
}

void build(){
    memset(lazy1,0,sizeof(lazy1));
    memset(lazy2,0,sizeof(lazy2));
    memset(sum1,0,sizeof(sum1));
    memset(sum2,0,sizeof(sum2));
}

void updata(int L,int R,int l,int r,int k,int y){
    if(l>=L&&r<=R){
        ++lazy1[k];
        sum1[k]+=(r-l+1);
        lazy2[k]+=y;
        sum2[k]+=(r-l+1)*y;
        return ;
    }
    pushdown(l,r,k);
    int mid=(l+r)>>1;
    if(L<=mid) updata(L,R,l,mid,k<<1,y);
    if(R>mid) updata(L,R,mid+1,r,k<<1|1,y);
    pushup(k);
}
int thisy;
ll myfind(int L,int R,int l,int r,int k){
    if(l>=L&&r<=R){
        return sum1[k]*thisy-sum2[k];
    }
    pushdown(l,r,k);
    ll res=0;
    int mid=(l+r)>>1;
    if(L<=mid) res+=myfind(L,R,l,mid,k<<1);
    if(R>mid) res+=myfind(L,R,mid+1,r,k<<1|1);
    pushup(k);
    return res;

}

ll x[maxn];
char s[3];

int main(){
    int n,m;
    while(scanf("%d",&n)!=EOF){
        int l,r;
        bool f=1;
        for(int i=1;i<=n;++i){
            scanf("%lld",&x[i]);
            x[i]+=x[i-1];
        }
        build();
        scanf("%d",&m);
        for(int i=1;i<=m;++i){
            thisy=i;
            scanf("%s%d%d",s,&l,&r);
            if(s[0]=='Q'){
                if(f) f=0;
                else printf(" ");
                printf("%lld",(x[r]-x[l-1])*i+myfind(l,r,1,n,1));
            }
            else{
                updata(l,r,1,n,1,i);
            }

        }
        printf("\n");
    }
    return 0;
}

 

你可能感兴趣的:(线段树)