[ABC335F] Hop Sugoroku 【根号分治】

[ABC335F] Hop Sugoroku 【根号分治】

TAGS: 根号分治 DP

APPRAIS: 很优美的暴力 DP

First. 朴素 DP

这里做一个转化:求不同集合的数量相当与求走到所有点的不同方案数之和。

dpi 表示走到 i 号的方案数。

显然, dpi 可以去更新所有 dpi+ai×x[i+ai×xn]

即:

for (int i = 1; i <= n; i ++) {
	for (int j = i + a[i]; j <= n; j += a[i]) {
		dp[j] += dp[i], dp[j] -= (dp[j] >= mod) ? mod : 0;
	}
}

时间复杂度:O(n2)

Second. 优化

sui,x,r 为所有小于 i 的数中模 xrdp 值之和。

对于 ai 的大小不同,我们尝试去做不同的解决方案。

设阈值为 x,对于 ix 的部分:

此时枚举所有 pxdpi=sup,imodx。这相当枚举所有与 ip 同余的所有 j 的位置的 dp 值之和。

剩余部分已经由 ai>xi 更新过了。

更新时,对于 aix

suai,imodai 上累加即可实现向后更新的转移。

对于 ai>x

此时直接暴力更新 dpi+ai×j 最多更新 nx 次。

所以总复杂度为 O(x+nx)

xn 时得到最优。

傻缺代码
#include <bits/stdc++.h>
using namespace std;
// 根号分治
// 1: a <= sqrt(n)
// 记 sum[x][y] 表示 % x = y 的 i 的 dp[i] 之和。
// 2: a > sqrt(n)
// 暴力更新,次数不会超过 sqrt(n) 次
/*
for (int i = 1; i <= n; i ++) {
	for (int j = i + a[i]; j <= n; j += a[i]) {
		dp[j] = (dp[j] + dp[i]) % mod;
	}
}
*/
const int N = 2e5 + 10, M = 500 + 10;
const int mod = 998244353;
int n, b, a, ans;
int dp[N], su[M][M];

signed main() {
	ios::sync_with_stdio(0);
	cin >> n;
	b = sqrt(n);
	dp[1] = 1;
	for (int  i = 1; i <= n; i ++) {
		cin >> a;
		for (int j = 1; j <= b; j ++) dp[i] = (dp[i] + su[j][i % j]) % mod;
		if(a >= b) for (int j = i + a; j <= n; j += a) dp[j] = (dp[j] + dp[i]) % mod;
		else su[a][i % a] = (su[a][i % a] + dp[i]) % mod;
		(ans += dp[i]) %= mod;
	}
	cout << ans << endl;
	return 0;
}

参考资料

posted @   固态H2O  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示