G. Natlan Exploring
G. Natlan Exploring
You are exploring the stunning region of Natlan! This region consists of cities, and each city is rated with an attractiveness . A directed edge exists from City to City if and only if and , where denotes the greatest common divisor (GCD) of integers and .
Starting from City , your task is to determine the total number of distinct paths you can take to reach City , modulo . Two paths are different if and only if the set of cities visited is different.
Input
The first line contains an integer () — the number of cities.
The second line contains integers () — the attractiveness of each city.
Output
Output the total number of distinct paths you can take to reach City , modulo .
Examples
Input
5
2 6 3 4 6
Output
5
Input
5
4 196 2662 2197 121
Output
2
Input
7
3 6 8 9 11 12 20
Output
7
Input
2
2 3
Output
0
Note
In the first example, the five paths are the following:
- City City
- City City City
- City City City City
- City City City City
- City City City
In the second example, the two paths are the following:
- City City City
- City City City City
解题思路
建图后会发现是一个拓扑图,因此可以 dp。然后就一直想着怎么优化建图结果做不出来。
事实上可以直接暴力 dp,定义 表示从 出发到达 的不同路径数量,那么转移方程就是 ,复杂度是 。可以知道任意一个数与 求 的结果只会是 的约数(本题中不考虑 ),因此我们可以只从前面含 约数的 转移过来。
具体来说,先用 累加前面含约数 的 的 ,枚举 的约数 ,有 。很明显这会有重复转移的问题,比如如果 ,意味着 含有约数 ,那么 会被累加了 次。可以通过容斥避免重复计算,公式如下:
容斥的复杂度是 ,其中 , 表示 的约数个数,大约是 的数量级。显然还是会超时。
思考一下,我们有必要枚举所有的约数吗?因为我们只关心 和 是否互质,而不关心具体的 是什么,因此只需枚举 的质因子就可以了。这样只要 ,那么 必然含 的某个质因子。由于 内一个数最多含 个不同的质因子,因此容斥的复杂度的数量级是 。
设 表示单个质数的乘积。 实现时需要维护 表示满足 对应的 的和。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5, M = 1e6 + 5, mod = 998244353;
int prime[M], minp[M], cnt;
bool vis[M];
int f[N], g[M];
void get_prime(int n) {
for (int i = 2; i <= n; i++) {
if (!vis[i]) prime[cnt++] = i, minp[i] = i;
for (int j = 0; prime[j] * i <= n; j++) {
vis[prime[j] * i] = true;
minp[prime[j] * i] = prime[j];
if (i % prime[j] == 0) break;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
get_prime(M - 1);
f[1] = 1;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
vector<int> fs;
while (x > 1) {
int p = minp[x];
fs.push_back(p);
while (minp[x] == p) {
x /= p;
}
}
for (int j = 1; j < 1 << fs.size(); j++) {
int p = 1, c = 0;
for (int k = 0; k < fs.size(); k++) {
if (j >> k & 1) p *= fs[k], c++;
}
if (c & 1) f[i] = (f[i] + g[p]) % mod;
else f[i] = (f[i] - g[p]) % mod;
}
for (int j = 1; j < 1 << fs.size(); j++) {
int p = 1;
for (int k = 0; k < fs.size(); k++) {
if (j >> k & 1) p *= fs[k];
}
g[p] = (g[p] + f[i]) % mod;
}
}
cout << (f[n] + mod) % mod;
return 0;
}
参考资料
Codeforces Round 988 (Div. 3) Editorial:https://codeforces.com/blog/entry/135533
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18554386
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-11-19 DFS 序
2023-11-19 [SHOI2007] 园丁的烦恼