int n;
ll a[N];
int L[M], R[M], B[N];
ll sum[M], tag[M];
int s, sz;
void init() {
	s = sqrt(n);
	sz = (n+s-1)/s;
	for (int i=1; i<=n; i++) {
		B[i] = (i-1)/s+1;
		sum[B[i]] += a[i];
	}
	for (int i=1; i<=sz; i++) {
		L[i] = (i-1)*s+1;
		R[i] = min(n, i*s);
	}
}

void add(int l, int r, ll val) {
	int x = B[l], y = B[r];
	if (x == y) {
		for (int i=l; i<=r; i++) {
			a[i] += val;
			sum[x] += val;
		}
	} else {
		for (int i=l; i<=R[x]; i++) {
			a[i] += val;
			sum[x] += val;
		}
		for (int i=L[y]; i<=r; i++) {
			a[i] += val;
			sum[y] += val;
		}
		for (int i=x+1; i<y; i++) {
			tag[i] += val;
		}
	}
}

ll ask(int l, int r, ll c) {
	int x = B[l], y = B[r];
	ll rt = 0;
	if (x == y) {
		for (int i=l; i<=r; i++) {
			rt += a[i] + tag[x];
		}
	} else {
		for (int i=l; i<=R[x]; i++) {
			rt += a[i] + tag[x];
		}
		for (int i=L[y]; i<=r; i++) {
			rt += a[i] + tag[y];
		}
		for (int i=x+1; i<y; i++) {
			rt += sum[i] + tag[i]*s;
		}
	}
	return rt%c;
}

分块算法主要思想

将长度为n的序列分为$\sqrt{n}$,对于区间的更新和查询都将对最多两个非完整块和$\sqrt{n}$个块操作

对非完整块可以直接暴力那么最多只需要$O(\sqrt{n})$时间复杂度,对单个完整块操作需要保证$O(1)$$O(log(\sqrt{n}))$时间复杂度,这样$\sqrt{n}$个块的时间复杂度在$\sqrt{n}$ 或 $\frac{\sqrt{n}}{2}log(n)$,当然其实对所有完整块可以直接暴力但是要保证暴力的次数很少。