D. Sums of Segments

题目链接:https://codeforces.com/contest/2026/problem/D

题意:

记a数组的前缀和[l,r]为s(l,r)

b数组为:[s(1,1),s(1,2)....,s(1,n)] , [s(2,2),s(2,3),s(2,4)...,s(2,n)] ,.....,[s(n-1,n-1),s(n-1,n)],[s(n,n)]

显然其长度为n(n-1)/2,现有q次询问,每次给定l和r,求b数组b[l]+b[l+1]....b[r]的和

思路:

分块+二分

我们需要快速求得sum_b(l,r)
不妨定义
pre_b(x)=b1+b2+b3...bx

则答案为pre_b(r)-pre_b(l-1)

为了求得pre_b(x)
只要求得 几个整块的总和 以及 剩下了不到一块的那一块的部分的和

把b数组按照上面中括号划分的方式分成n块
求得整块整块的总和,以及块的前缀和

//第i块的总和
val[i]=(pre_pre[n]-pre_pre[i-1])-len*pre[i-1];

通过二分+等差数列求和公式 可以快速求得x位置前的块的编号p

剩下来不到一块的那块编号为p+1,

把这块的长度求出,即可通过运算求得这块的和

时间复杂度O(qlogn)
(大部分块由二分logn直接预处理出来了)

int a[maxn];
int pre[maxn];
int pre_pre[maxn];
int val[maxn];
int val_pre[maxn];
int n,q;
int cal(int x){//二分求p
    int l=0,r=n;
    int res=0;
    while(l<=r){
        int mid=l+r>>1;
        if(mid*n-mid*(mid-1)/2<=x){
            res=mid;
            l=mid+1;
        }else r=mid-1;
    }
    return res;
}
//1:s(1,1)
//2:s(1,1)+s(1,2)
//3:s(1,1)+s(1,2)+s(1,3)
//4:s(1,1)+s(1,2)+s(1,3)+s(1,4)


int work(int x,int p){
    //x:pos p:idx
    int ans=0;
    ans+=val_pre[p];
    int st=(p*(n+(n-p+1)))/2+1;
    int len=x-st+1;
    int idx=p+1;
    int last=idx+len-1;
    int res=((pre_pre[last]-pre_pre[idx-1])-len*(pre[idx-1]));
    return ans+res;
}
void solve(){
    cin>>n;
    rep(i,1,n)cin>>a[i];
    rep(i,1,n){
        pre[i]=pre[i-1]+a[i];
        pre_pre[i]=pre_pre[i-1]+pre[i];
    }
    rep(i,1,n){
        int len=n-i+1;
        val[i]=(pre_pre[n]-pre_pre[i-1])-len*pre[i-1];
        val_pre[i]=val_pre[i-1]+val[i];
    }
    cin>>q;
    while(q--){
        int l,r;cin>>l>>r;
        cout<<work(r,cal(r))-work(l-1,cal(l-1))<<endl;
    }
}
posted @ 2025-05-17 16:37  Marinaco  阅读(19)  评论(0)    收藏  举报
//雪花飘落效果