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;
}
}

浙公网安备 33010602011771号