1 条题解

  • 0
    @ 2026-7-6 21:54:23

    分块+根号分治简单维护即可。

    考虑对xx的大小分类讨论。

    xnx\geqslant \sqrt n,则我们暴力给每个位置加,需要加的次数为O(n)O(\sqrt n)次。由于需要查询区间和,用分块维护,总修改、查询复杂度为O(mn)O(m\sqrt n)

    x<nx< \sqrt n,我们需要用另外的方法维护。

    注意到单次修改是针对整个序列的元素,所以对x,yx,y相同的修改,我们可以累加它的贡献。

    那么我们每次查询的时候,需要对于所有的xx进行一遍查询。故我们需要做到单次O(1)O(1)

    我们对每个xx,维护yy的前缀、后缀和。对于一次询问,我们可以当成把序列分成了若干个大小为xx的块。中间的整块元素,每个块里肯定所有的yy都有,增加的贡献就是关于xx的修改总和。所有块的贡献相同,可以O(1)O(1)算。边角的话,由于我们记录了前缀、后缀和,也可以O(1)O(1)算。两个端点在同一个块中,则直接前缀和相减即可。

    总时间复杂度O(mn)O(m\sqrt n)

    由于常数原因,阈值不应该取太大。

    Code:

    #include<iostream>
    const int siz=128,M=siz+2,N=2e5+5,Z=N/siz+2,md=1e9+7;
    #define bel(x)((x-1)/siz+1)
    inline void upd(int&a){a+=a>>31&md;}
    int n,blocks,L[Z],R[Z],a[N],sum[Z],m;
    int pre[M][M],suf[M][M];
    inline int sigma(int l,int r){
    	const int bL=bel(l),bR=bel(r);
    	int ret=0;
    	if(bL==bR)
    	for(int i=l;i<=r;++i)upd(ret+=a[i]-md);
    	else{
    		for(int i=R[bL];i>=l;--i)upd(ret+=a[i]-md);
    		for(int i=L[bR];i<=r;++i)upd(ret+=a[i]-md);
    		for(int i=bL+1;i<bR;++i)upd(ret+=sum[i]-md);
    	}
    	return ret;
    }
    int main(){
    	std::ios::sync_with_stdio(0),std::cin.tie(0),std::cout.tie(0);
    	std::cin>>n>>m;
    	blocks=bel(n);
    	for(int i=1;i<=blocks;++i)L[i]=R[i-1]+1,R[i]=i*siz;R[blocks]=n;
    	for(int i=1;i<=n;++i)std::cin>>a[i];
    	for(int i=1;i<=blocks;++i)
    	for(int j=L[i];j<=R[i];++j)upd(sum[i]+=a[j]-md);
    	while(m--){
    		int op;
    		std::cin>>op;
    		if(op==1){
    			int x,y,z;
    			std::cin>>x>>y>>z,z-=md;
    			if(x>=siz)
    			for(int i=y;i<=n;i+=x)upd(a[i]+=z),upd(sum[bel(i)]+=z);
    			else{
    				int*pr=pre[x],*sf=suf[x];
    				for(int i=x;i>=y;--i)upd(pr[i]+=z);
    				for(int i=1;i<=y;++i)upd(sf[i]+=z);
    			}
    		}else{
    			int l,r;
    			std::cin>>l>>r;
    			int ans=sigma(l,r);
    			for(int i=1;i<siz;++i){
    				const int bL=(l-1)/i+1,bR=(r-1)/i+1;
    				if(bL==bR)upd(ans+=pre[i][(r-1)%i+1]-md),upd(ans-=pre[i][(l-1)%i]);else
    				ans=(ans+(bR-bL-1LL)*pre[i][i])%md,upd(ans+=pre[i][(r-1)%i+1]-md),upd(ans+=suf[i][(l-1)%i+1]-md);
    			}
    			std::cout<<ans<<'\n';
    		}
    	}
    	return 0;
    }
    
    
    • 1

    信息

    ID
    484
    时间
    1000ms
    内存
    80MiB
    难度
    9
    标签
    递交数
    17
    已通过
    3
    上传者