logo AlgoBeat OnlineJudge
登录 注册

题解

作者: _ZXY_  ·  发布于 2026-05-17 19:42:33  ·  最后修改于 2026-05-17 21:32:42
已通过
审核员:Shadow_T 管理员 · 2026-05-17 21:32:42

给出一个长为 的数列。
个询问,每行输入四个数字 ,以空格隔开。 若 ,表示将位于 的之间的数字都加
,表示询问 的值( 忽略)。
保证 。每次操作后的 满足
考虑暴力。
我们可以按题目说的模拟,每次遍历 加, 就输出 ,复杂度 ,明显无法通过。

考虑复杂度正确的做法。
我们可以将数组分成一段一段的“块”。
就像这样:
对于每一个块,我们可以去维护它块内元素的和。在求区间加的时候,对于“不完整”的块直接暴力加,中间的“完整块”打上一个懒标记。这样在求 的值的时候,只需要用 加上 所在块的懒标记了。

时间复杂度

这个思想听上去不错,但仍然有一些小细节。

如何计算下标对应的块编号?
我们设预定块长为 ,则编号为 。是什么意思呢?我们先将下标转换为 开头的,这样 就是从 开始的块编号,加一就是 开头的了。

如果 不是 的倍数怎么办?
其实最后一个块不一定是完整的,不影响计算。

块长怎么确定?
块长最好是
不难发现上述操作区间修改的时间复杂度是 的。
由二元均值不等式,对 有: ,代入得: 等号成立条件: 所以最优块长为

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e5+7;
int a[N],b[N],id[N],n,len,s[N];
void add(int l,int r,int x){
    if(id[l]==id[r]){
        for(int i=l;i<=r;i++)a[i]+=x,s[id[l]]+=x;//同一块暴力加
        return;
    }
    for(int i=l;id[i]==id[l];i++)a[i]+=x,s[id[l]]+=x;//左边“不完整块”暴力加
    for(int i=id[l]+1;i<id[r];i++)b[i]+=x,s[i]+=len*x;//中间大块打懒标记,更新块内元素和
    for(int i=r;id[i]==id[r];i--)a[i]+=x,s[id[r]]+=x;//右边“不完整块”暴力加
}
signed main(){
    cin>>n;
    len=sqrt(n);
    for(int i=1;i<=n;i++){
        cin>>a[i];
        id[i]=(i-1)/len+1;
        s[id[i]]+=a[i];
    }
    for(int i=1;i<=n;i++){
        int opt,l,r,c;
        cin>>opt>>l>>r>>c;
        if(opt==0)add(l,r,c);
        else print("{}\n",a[r]+b[id[r]]);//求值
    }
}

共 1 条评论

_ZXY_

注意:std::print 在 C++23 中引入

登录 后即可评论。