luogu P5309 [Ynoi2011]初始化
题面传送门
不小心抢了个最优解,比第二少了\(0.6s\)
分块+根号分治套路题。
看到这种跳着加的就知道是根号分治了。
对于\(x>s\)的直接加,用分块维护。
对于\(x<s\)的,维护数组\(f_{i,j}\)表示跳\(i\)个,从\(j\)开始加了几次。为了之后统计方便,还要前缀和。
统计时,一部分是分块直接统计。另一部分可以在\(f_{i,j}\)上算,考虑整块和零散的情况。直接统计前缀和即可。
关于\(s\)我取80左右,实测跑得飞快
代码实现:
#include<cstdio>
#include<cmath>
#define mod 1000000007
using namespace std;
int n,m,k,x,y,op,sg;
long long q[139][139],fs[1039],ans,a[203009],z;
int main(){
// freopen("1.in","r",stdin);
register int i,j;
scanf("%d%d",&n,&m);
sg=70;k=sqrt(n);
for(i=1;i<=n;i++)scanf("%lld",&a[i]),fs[i/k]+=a[i];
for(i=1;i<=m;i++){
scanf("%d",&op);
if(op==1){
scanf("%d%d%lld",&x,&y,&z);
if(x>=sg) for(j=y;j<=n;j+=x) a[j]+=z,fs[j/k]+=z;
else {
y%=x;
for(j=y;j<x;j++) q[x][j]+=z;
}
}
else{
ans=0;
scanf("%d%d",&x,&y);
if(x/k==y/k)for(j=x;j<=y;j++) ans+=a[j];
else{
for(j=x;j<x/k*k+k;j++) ans+=a[j];
for(j=x/k+1;j<y/k;j++) ans+=fs[j];
for(j=y/k*k;j<=y;j++) ans+=a[j];
}
for(j=1;j<=sg;j++){
if(x/j==y/j) ans+=q[j][y%j]-q[j][x%j-1];
else ans+=q[j][j-1]*(y/j-x/j)-q[j][x%j-1]+q[j][y%j];
}
printf("%lld\n",ans%mod);
}
}
}