《算法竞赛进阶指南》0x44分块 POJ3468解法二

题目链接:https://www.acwing.com/problem/content/244/

树状数组大约比分块快上几倍,分块数据结构也具有很强的利用余地,我们这个问题中,没个分块上有两个属性,一个是sum一个是add,其中一个分块的sum域是真实的,但是其中的a[i]是不真实的,真实的a[i]是a[i]+add[pos[i]]。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn = 100010;
typedef long long ll;
int L[maxn],R[maxn];
ll a[maxn],sum[maxn],add[maxn];
int pos[maxn];
int n,m,t;
void update(int l,int r,int C){
    int p=pos[l],q=pos[r];
    if(p==q){
        for(int i=l;i<=r;i++)a[i]+=C;
        sum[p]+=C*(r-l+1);
        return;
    }else{
        for(int i=p+1;i<=q-1;i++){
            sum[i]+=C*(R[i]-L[i]+1);
            add[i]+=C;
        }
        for(int i=l;i<=R[p];i++)a[i]+=C;
        sum[p]+=C*(R[p]-l+1);
        for(int i=L[q];i<=r;i++)a[i]+=C;
        sum[q]+=C*(r-L[q]+1);
    }
} 
ll query(int l,int r){
    ll ans=0;
    int p=pos[l],q=pos[r];
    if(p==q){
        for(int i=l;i<=r;i++)ans+=a[i];
        ans+=add[p]*(r-l+1);
        return ans;
    }else{
        for(int i=p+1;i<=q-1;i++)ans+=sum[i];
        for(int i=l;i<=R[p];i++)ans+=a[i];
        ans+=add[p]*(R[p]-l+1);
        for(int i=L[q];i<=r;i++)ans+=a[i];
        ans+=add[q]*(r-L[q]+1);
        return ans;
    }
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    
    //构建分块 
    t=sqrt(n*1.0);
    for(int i=1;i<=t;i++){
        L[i]=(i-1)*t+1;
        R[i]=i*t;
    }
    if(R[t]<n)++t,L[t]=R[t-1]+1,R[t]=n;
    
    //设置分块上的属性,以及每个位置的归属 
    for(int i=1;i<=t;i++){
        for(int j=L[i];j<=R[i];j++){
            pos[j]=i;
            sum[i]+=a[j];
        }
        add[i]=0;
    }
    
    while(m--){
        char s[10];
        int l,r,d;
        scanf("%s%d%d",s,&l,&r);
        if(s[0]=='C'){
            scanf("%d",&d);
            update(l,r,d);
        }else{
            printf("%lld
",query(l,r));
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/randy-lo/p/13304752.html