把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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);
		}
	}
}
posted @ 2020-10-07 22:04  275307894a  阅读(57)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end