隐藏页面特效

CF2037G - Natlan Exploring 题解

又来到我们最喜欢的数论环节了。

题面

纳特兰地区由 n 座城市组成,每座城市的吸引力值为 ai 。从城市 i 到城市 j 之间存在一条有向边,当且仅当 i<jgcd(ai,aj)1 ,其中 gcd(x,y) 表示整数 xy最大公约数 (GCD)

从城市 1 出发,你的任务是确定到达城市 n 的不同路径的总数,答案对 998,244,353 取模。当且仅当所访问的城市集合不同时,两条路径才是不同的。


题解

一眼看过去会是一个方案数 dp,但是不这么好直接干出来,因为边是不定的,而且无法全部建出来。

不妨设 f(i) 表示到达城市 i 的方案数,容易得到:

f(i)=i1j=1f(j)[gcd(ai,aj)1]

看见这玩意直接来一发莫比乌斯反演。

f(i)=i1j=1f(j)dgcd(ai,aj)d1μ(d)

移一下项,把 ai 放到前面去,aj 留在后面:

f(i)=daid1μ(d)i1j=1f(j)[daj]

此刻,我们发现一个非常好的一点,后者是 d,i 相关独立的,可以提出来。

不妨设 g(n,d)=nj=1f(j)[daj],容易发现:

g(i,d)=g(i1,d)+f(i)[dai]

在计算 f(i) 的过程中可以顺带把 g(i,d) 给计算出来。

每次算完 f(i) 更新 g(i1,d),因此可以降下一维,满足空间需求。

线性筛筛出莫比乌斯函数,预处理因数 O(AlnA),或枚举因数 O(nA) 即可。

不过预处理比枚举因数慢是令我没想到的。

参考代码(预处理)

复制#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 10, M = 2e5 + 10, mod = 998244353; int n, a[M]; int st[N], primes[N], cnt, mu[N]; vector<int> factors[N]; ll f[M], g[N]; void init() { mu[1] = 1; for (int i = 2; i < N; i ++ ) { if (!st[i]) primes[cnt ++ ] = i, mu[i] = 1; for (int j = 0; primes[j] * i < N; j ++ ) { st[i * primes[j]] = 1; if (i % primes[j] == 0) break; mu[i * primes[j]] = -mu[i]; } } for (int i = 2; i < N; i ++ ) for (int j = i; j < N; j += i) factors[j].push_back(i); } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> n, init(); for (int i = 1; i <= n; i ++ ) cin >> a[i]; f[1] = 1; for (int i = 2; i <= n; i ++ ) { for (auto d : factors[a[i - 1]]) if (a[i - 1] % d == 0) (g[d] += f[i - 1]) %= mod; for (auto d : factors[a[i]]) if (mu[d]) (f[i] += mu[d] * g[d]) %= mod; } cout << (f[n] + mod) % mod; return 0; }

参考代码(枚举)

复制#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6 + 10, M = 2e5 + 10, mod = 998244353; int n, a[M]; int st[N], primes[N], cnt, mu[N]; vector<int> factors[N]; ll f[M], g[N]; void init() { mu[1] = 1; for (int i = 2; i < N; i ++ ) { if (!st[i]) primes[cnt ++ ] = i, mu[i] = 1; for (int j = 0; primes[j] * i < N; j ++ ) { st[i * primes[j]] = 1; if (i % primes[j] == 0) break; mu[i * primes[j]] = -mu[i]; } } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> n, init(); for (int i = 1; i <= n; i ++ ) cin >> a[i]; f[1] = 1; for (int i = 2; i <= n; i ++ ) { for (int d = 1; d * d <= a[i - 1]; d ++ ) { if (a[i - 1] % d) continue; if (d != 1) (g[d] += f[i - 1]) %= mod; if (d * d != a[i - 1]) (g[a[i - 1] / d] += f[i - 1]) %= mod; } for (int d = 1; d * d <= a[i]; d ++ ) { if (a[i] % d) continue; if (d != 1) (f[i] += mu[d] * g[d]) %= mod; if (d * d != a[i]) (f[i] += mu[a[i] / d] * g[a[i] / d]) %= mod; } } cout << (f[n] + mod) % mod; return 0; }

__EOF__

本文作者Yip.Chip
本文链接https://www.cnblogs.com/YipChipqwq/p/-/CF2037G.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   YipChip  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示