根号分治本质上来说就是暴力的做法,问题的规模和数字的大小成反比,所以我们可以在问题规模较小时,采用暴力的方法转移,当问题规模较大时,通过别的方式,一般是采用空间换时间的方法,实现快速的转移。总的时间复杂度在 n * 根号n
1.https://www.luogu.com.cn/problem/AT_abc335_f
启蒙题,先考虑暴力的刷表转移
dp[i] ->dp[i + x * a[i]]
这样的转移在最坏的情况是o(n) ,很明显不符合时间
在考虑第二种转移
dp[i] += dp[j]? ? ( i?% a[i] == j % a[i] )?
很明显可以考虑,对于一个数,他所能跟新的答案一定是和他余,先将这个数开一个新数组存起来
v[ i ][ j ]? 代表 模 i 意义下 余数为 j 的方案数
v[ a[ i ] ][ j % a[ i ] ] += dp[ i?]
考虑第二种方法要存下所有的a[i] 没有那么大空间,于是就可以考虑将两种方法结合,
当转移规模较小时,采用1? ,当转移规模较大时,采用2
很明显问题的规模和a[ i ] 呈负相关。
// Problem: [ABC335F] Hop Sugoroku
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/AT_abc335_f
// Memory Limit: 1 MB
// Time Limit: 2500 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
using VI = vector<int>;
using ll = long long;
using PII = pair<ll,ll>;
const ll mod = 998244353;
const int N = 2e5 + 10;
const int B = 450;
int n;
ll v[B][B];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
VI a(n + 2),dp(n + 2);
ll res = 0;
for(int i = 1 ; i <= n ; i++) cin>>a[i];
dp[1] = 1;
for(int i = 1 ; i <= n ; i++){
for(int j = 1 ; j < B ; j++){
//cout<<i;
dp[i] += v[j][i % j];
dp[i] %= mod;
}
if(a[i] < B){
v[a[i]][i % a[i]] += dp[i];
v[a[i]][i % a[i]] %= mod;
}else{
for(int j = i + a[i] ; j <= n ; j+=a[i]){
dp[j] += dp[i];
dp[j] %= mod;
}
}
res += dp[i];
res %= mod;
}
cout<<res;
}
//5 4 3 2 1
//5 4 3 2 1
??——————————————————————————————————————————
问题规模很小时,暴力计算
当问题规模很大时,使用前缀和
s[d][i] 前 i 项 公差为d的加权和????????s[d][i] = s[d][i - d] + i / d * a[i]
g[d][i] = g[d][i - d] + a[i];
如何从第 s 项开始则 某一项为 a[s + (k - 1) * d]? * k
前缀和为 a[s + (k - 1) * d] * (s + (k - 1) * d / d? = s / d - 1 + k
做差相减即可
当d较大时, 问题规模小,可以暴力转移,
当d较小时, 问题规模大,利用前缀转移
// Problem: F. Sum of Progression
// Contest: Codeforces - Codeforces Round 920 (Div. 3)
// URL: https://codeforces.com/contest/1921/problem/F
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
using VI = vector<int>;
using ll = long long;
using PII = pair<ll,ll>;
const ll mod = 998244353;
const int N = 1e5 + 10;
const int B = 330;
ll sum[B][N];
ll g[B][N];
ll a[N];
void solve(){
int n,q;
cin>>n>>q;
for(int i = 1 ; i <= n ; i++) cin>>a[i];
int w = sqrt(n);
for(int i = 1 ; i <= w ; i++){
for(int j = 1 ; j <= n ; j++){
sum[i][j] = sum[i][max(j - i , 0)] + (j / i) * a[j];
g[i][j] = g[i][max(j - i , 0)] + a[j];
}
}
while(q--){
int s,d,k;
cin>>s>>d>>k;
ll res = 0;
if(d > w){
for(int i = 1 , j = s ; i <= k ; i++ , j += d){
res += a[j] * (ll)i;
}
}else{
//cout<<sum[2][0]<<" ";
res = sum[d][s + (k - 1) * d] - sum[d][max(s - d , 0)];
res -= (ll)(s / d - 1) * (g[d][s + (k - 1) * d] - g[d][max(s - d , 0)]);
}
cout<<res<<" ";
}
cout<<endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--){
solve();
}
}
//5 4 3 2 1
//5 4 3 2 1