根号分治

根号分治本质上来说就是暴力的做法,问题的规模和数字的大小成反比,所以我们可以在问题规模较小时,采用暴力的方法转移,当问题规模较大时,通过别的方式,一般是采用空间换时间的方法,实现快速的转移。总的时间复杂度在 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

  ——————————————————————————————————————————

2.Problem - F - Codeforces

问题规模很小时,暴力计算

当问题规模很大时,使用前缀和

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

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值