G. Natlan Exploring

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

posted @ 2024-11-19 10:50  onlyblues  阅读(79)  评论(0编辑  收藏  举报
Web Analytics