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 ai. A directed edge exists from City i to City j if and only if i<j and gcd(ai,aj)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 998244353. Two paths are different if and only if the set of cities visited is different.

Input

The first line contains an integer n (2n2105) — the number of cities.

The second line contains n integers a1,a2,,an (2ai106) — the attractiveness of each city.

Output

Output the total number of distinct paths you can take to reach City n, modulo 998244353.

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 City 5
  • City 1 City 2 City 5
  • City 1 City 2 City 3 City 5
  • City 1 City 2 City 4 City 5
  • City 1 City 4 City 5

In the second example, the two paths are the following:

  • City 1 City 3 City 5
  • City 1 City 2 City 3 City 5

 

解题思路

  建图后会发现是一个拓扑图,因此可以 dp。然后就一直想着怎么优化建图结果做不出来。

  事实上可以直接暴力 dp,定义 f(i) 表示从 1 出发到达 i 的不同路径数量,那么转移方程就是 f(i)=j=1i1[gcd(ai,aj)1]f(j),复杂度是 O(n2logn)。可以知道任意一个数与 aigcd 的结果只会是 ai 的约数(本题中不考虑 1),因此我们可以只从前面含 ai 约数的 aj 转移过来。

  具体来说,先用 g(d) 累加前面含约数 dajf(j),枚举 ai 的约数 d,有 f(i)f(i)+g(d)。很明显这会有重复转移的问题,比如如果 gcd(ai,aj)=10,意味着 aj 含有约数 2,5,10,那么 f(j) 会被累加了 3 次。可以通过容斥避免重复计算,公式如下:ig(di)i<jg(didj)+i<j<kg(didjdk)i<j<k<ug(didjdkdu)+

  容斥的复杂度是 O(2d(A)),其中 A=max{ai}d(A) 表示 A 的约数个数,大约是 A3 的数量级。显然还是会超时。

  思考一下,我们有必要枚举所有的约数吗?因为我们只关心 aiaj 是否互质,而不关心具体的 gcd 是什么,因此只需枚举 ai 的质因子就可以了。这样只要 gcd(ai,aj)1,那么 aj 必然含 ai 的某个质因子。由于 106 内一个数最多含 7 个不同的质因子,因此容斥的复杂度的数量级是 O(27)

  设 d=p1p2 表示单个质数的乘积。 实现时需要维护 g(d) 表示满足 dai 对应的 f(i) 的和。

  AC 代码如下,时间复杂度为 O(nlogn+27n)

#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 @   onlyblues  阅读(103)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2023-11-19 DFS 序
2023-11-19 [SHOI2007] 园丁的烦恼
Web Analytics
点击右上角即可分享
微信分享提示