G. Natlan Exploring
You are exploring the stunning region of Natlan! This region consists of $n$ cities, and each city is rated with an attractiveness $a_i$. A directed edge exists from City $i$ to City $j$ if and only if $i < j$ and $\gcd(a_i,a_j)\neq 1$, where $\gcd(x, y)$ denotes the greatest common divisor (GCD) of integers $x$ and $y$.
Starting from City $1$, your task is to determine the total number of distinct paths you can take to reach City $n$, modulo $998\,244\,353$. Two paths are different if and only if the set of cities visited is different.
Input
The first line contains an integer $n$ ($2 \leq n \leq 2 \cdot 10^5$) — the number of cities.
The second line contains $n$ integers $a_1, a_2, \ldots, a_n$ ($2 \leq a_i \leq 10^6$) — the attractiveness of each city.
Output
Output the total number of distinct paths you can take to reach City $n$, modulo $998\,244\,353$.
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 $1\rightarrow$ City $5$
- City $1\rightarrow$ City $2\rightarrow$ City $5$
- City $1\rightarrow$ City $2\rightarrow$ City $3\rightarrow$ City $5$
- City $1\rightarrow$ City $2\rightarrow$ City $4\rightarrow$ City $5$
- City $1\rightarrow$ City $4\rightarrow$ City $5$
In the second example, the two paths are the following:
- City $1\rightarrow$ City $3\rightarrow$ City $5$
- City $1\rightarrow$ City $2\rightarrow$ City $3\rightarrow$ City $5$
解题思路
建图后会发现是一个拓扑图,因此可以 dp。然后就一直想着怎么优化建图结果做不出来。
事实上可以直接暴力 dp,定义 $f(i)$ 表示从 $1$ 出发到达 $i$ 的不同路径数量,那么转移方程就是 $f(i) = \sum\limits_{j=1}^{i-1}{[\gcd(a_i, a_j) \ne 1]f(j)}$,复杂度是 $O(n^2 \log{n})$。可以知道任意一个数与 $a_i$ 求 $\gcd$ 的结果只会是 $a_i$ 的约数(本题中不考虑 $1$),因此我们可以只从前面含 $a_i$ 约数的 $a_j$ 转移过来。
具体来说,先用 $g(d)$ 累加前面含约数 $d$ 的 $a_j$ 的 $f(j)$,枚举 $a_i$ 的约数 $d$,有 $f(i) \gets f(i) + g(d)$。很明显这会有重复转移的问题,比如如果 $\gcd(a_i, a_j) = 10$,意味着 $a_j$ 含有约数 $2,5,10$,那么 $f(j)$ 会被累加了 $3$ 次。可以通过容斥避免重复计算,公式如下:$$\sum\limits_{i}{g(d_i)} - \sum\limits_{i<j}{g(d_i \cdot d_j)} + \sum\limits_{i<j<k}{g(d_i \cdot d_j \cdot d_k)} - \sum\limits_{i<j<k<u}{g(d_i \cdot d_j \cdot d_k \cdot d_u)} + \cdots$$
容斥的复杂度是 $O(2^{d(A)})$,其中 $A = \max\{a_i\}$,$d(A)$ 表示 $A$ 的约数个数,大约是 $\sqrt[3]{A}$ 的数量级。显然还是会超时。
思考一下,我们有必要枚举所有的约数吗?因为我们只关心 $a_i$ 和 $a_j$ 是否互质,而不关心具体的 $\gcd$ 是什么,因此只需枚举 $a_i$ 的质因子就可以了。这样只要 $\gcd(a_i, a_j) \ne 1$,那么 $a_j$ 必然含 $a_i$ 的某个质因子。由于 $10^6$ 内一个数最多含 $7$ 个不同的质因子,因此容斥的复杂度的数量级是 $O(2^7)$。
设 $d = p_1 \cdot p_2 \cdots$ 表示单个质数的乘积。 实现时需要维护 $g(d)$ 表示满足 $d \mid a_i$ 对应的 $f(i)$ 的和。
AC 代码如下,时间复杂度为 $O(n \log{n} + 2^7 n)$:
#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
标签:Natlan,Exploring,gcd,City,int,约数,Output,rightarrow From: https://www.cnblogs.com/onlyblues/p/18554386