题目链接 : [Ynoi2011] 初始化
神仙trick + 卡常题,前缀后缀和根本没听过。
根号分治 + 分块。
对于修改操作,发现是跳着修改,考虑根号分治。
- 若\(x \ge \sqrt{n}\),直接暴力更改,复杂度\(O(\sqrt{n})\)。
- 反之,可以将序列抽象成一堆大小为\(x\)的段,如图,\(l,r\)是查询的区间。
发现\(l\sim r\)中间的整块一定都会被修改到,但是散块会被部分修改。由于题面中有一个重要的性质\(y\le x\),所以\(y\)一定在第一块中。
记两个数组\(pre_{i,j}\)表示记录当以\(i\)为块长分割序列时,前\(j\)个位置被更改的前缀和,\(suf_{i,j}\)为后缀和。
对于一个查询\(l,r\),如果是整块,直接前缀和求差,如果不在同一块,那么直接求\(l\)到其所在块末的后缀和,\(r\)到其所在块的前缀和,还有中间整块的贡献。
由于常数原因,\(x\)的阈值不应设太大,实测大约120左右跑的最快,序列分块时的块长取\(\sqrt{n}\)即可。
本题并不卡常,cin/cout关了同步流就可过。
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;const int N = 2e5 + 10,M = 500,mod = 1e9 + 7;
inline void Mod(int &x){if(x > mod) x -= mod;}
int n,m,a[N],len,pos[N],L[M],R[M],sum[M],limit,pre[M][M],suf[M][M],siz;
inline void solve(){cin>>n>>m;for(int i = 1;i <= n; ++i) cin>>a[i];limit = 128;len = sqrt(n),siz = n/len;for(int i = 1;i <= siz; ++i) L[i] = R[i - 1] + 1,R[i] = L[i] + len - 1;if(R[siz] < n) siz++,L[siz] = R[siz - 1] + 1,R[siz] = n;for(int i = 1;i <= siz; ++i){for(int j = L[i];j <= R[i]; ++j){pos[j] = i;Mod((sum[i] += a[j]));}}auto update1 = [&](int x,int y,int z)-> void{for(int i = y;i <= n;i += x){Mod(a[i] += z);Mod(sum[pos[i]] += z);}};auto update2 = [&](int x,int y,int z) -> void{for(int i = x;i >= y; --i) Mod(pre[x][i] += z);for(int i = 1;i <= y; ++i) Mod(suf[x][i] += z);};auto Q = [&](int left,int right) -> int{int p = pos[left],q = pos[right],res = 0;if(p == q){for(int i = left;i <= right; ++i) Mod(res += a[i]);return res;}for(int i = left;i <= R[p]; ++i) Mod(res += a[i]);for(int i = L[q];i <= right; ++i) Mod(res += a[i]);for(int i = p + 1;i < q; ++i) Mod(res += sum[i]);return res;};auto query = [&](int left,int right) -> int{int res = Q(left,right);for(int i = 1;i < limit; ++i){const int p = (left - 1)/i+1,q = (right-1)/i+1;if(p == q){Mod(res += pre[i][(right-1)%i+1]);Mod((res -= pre[i][(left-1)%i]) += mod);}else{res = (res + (q - p - 1ll)*pre[i][i]%mod)%mod;Mod(res += pre[i][(right-1)%i+1]);Mod(res += suf[i][(left-1)%i+1]);}}return res;};for(int i = 1,op,l,r,x,y,z;i <= m; ++i){cin>>op;if(op^2){cin>>x>>y>>z;Mod(z);if(x >= limit) update1(x,y,z);else update2(x,y,z);}else{cin>>l>>r;cout<<query(l,r)<<'\n';}}
}
signed main(){cin.tie(nullptr)->sync_with_stdio(false);cout.tie(nullptr)->sync_with_stdio(false);solve();
}