[Ynoi2012]D1T1

题目

套路的根号分治啊

我们设置一个值\(S\)

对于\(S\leq x\)的操作,我们直接暴力修改,显然这样只会修改\(\frac{n}{S}\)次,所以我们需要一个能够\(O(1)\)修改的数据结构,自然是首选分块

对于\(S>x\)的操作,我们对于每一个\(x\)维护一个块,我们维护这个块的前缀和就好了,复杂度是\(O(S)\)

查询的时候先用分块查一下区间和,复杂度\(O(S+\frac{n}{S})\),之后对于每一个\(x<S\)的块我们单独\(O(1)\)求一下,复杂度是\(O(S)\)

理论上自然是\(S=\sqrt{n}\)的时候取到了最优的复杂度,但是我们根据一些奇怪的常数原因,可以魔改一波块大小

代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=2e5+5;
const int mod=1e9+7;
int n,m,sz;
int pr[500][500],sum[500];
int pre[maxn],id[maxn],l[5000];
inline int qm(int a) {return a>=mod?a-mod:a;}
struct Block {
	int a[maxn],tag[5000];
	inline void change(int pos,int v) {
		a[pos]=qm(a[pos]+v);
		tag[id[pos]]=qm(tag[id[pos]]+v);
	}
	inline int ask(int x) {
		int now=0;
		for(re int i=1;i<id[x];i++) now=qm(now+tag[i]);
		for(re int i=l[id[x]];i<=x;i++) now=qm(now+a[i]);
		return now;
		
	}
}B;
inline int find(int x,int y) {return !(y%x)?y/x:y/x+1;}
inline int solve(int x,int y) {
	if(!y) return 0;
	return qm(1ll*(find(x,y)-1)*sum[x]%mod+pr[x][(y%x)?y%x:x]);
}
int main() {
	n=read(),m=read();
	for(re int i=1;i<=n;i++) pre[i]=(pre[i-1]+read())%mod;
	sz=std::sqrt(n);int L=1,tot=0;sz=250;
	while(L<=n) {
		l[++tot]=L;
		for(re int i=L;i<=min(L+sz-1,n);i++) id[i]=tot;
		L+=sz;
	}
	int op,x,y,v;
	while(m--) {
		op=read(),x=read(),y=read();
		if(op==2) {
			int ans=(pre[y]-pre[x-1]+mod)%mod;
			ans=(ans+B.ask(y)-B.ask(x-1))%mod;
			for(re int i=1;i<sz;i++) ans=(ans+solve(i,y)-solve(i,x-1))%mod;
			printf("%d\n",(ans+mod)%mod);
		}
		if(op==1) {
			v=read();
			if(x>=sz) {while(y<=n) B.change(y,v),y+=x;continue;}
			sum[x]=(sum[x]+v)%mod;
			for(re int i=y;i<=x;i++) pr[x][i]=qm(pr[x][i]+v);
		}
	}
	return 0;
}
posted @ 2019-05-12 20:21  asuldb  阅读(167)  评论(0编辑  收藏  举报