CF2037G Natlan Exploring
CF2037G Natlan Exploring
题意
题目大意
给定 \(n\) 个城市,每个城市有一个吸引力值 \(a_i\)。城市 \(i\) 到城市 \(j\) 有一条有向边当且仅当 \(i < j\) 且 \(\gcd(a_i, a_j) \neq 1\)。求从城市 \(1\) 到城市 \(n\) 的不同路径数量(路径定义为经过的城市序列不同),结果对 \(998244353\) 取模。
数据范围
- 城市数量 \(n\):\(2 \leq n \leq 2 \times 10^5\)
- 吸引力值 \(a_i\):\(2 \leq a_i \leq 10^6\)
思路
考虑 \(dp\),转移显然:\(dp_i=\sum_{\gcd (a_i,a_j)\neq 0}^i dp_j\).
考虑优化:可以维护 \(s\) 数组,令 \(s_i\) 表示含有约束 \(i\) 的所有 \(a_j\) 的 \(dp\) 和。
可以直接容斥。
但发现,如果 \(s_i\) 中含有平方因子其实是没有意义的(指数并不决定 \(\gcd\) 是否为 \(1\),只影响 \(\gcd\) 大小),联系到莫比乌斯函数,发现这一容斥其实就是 \(\sum_{j|a_i}^i-\phi(j)\times s_j\)。
每次处理完新的 \(dp_i\) 不要忘记更新 \(s\).
\(\phi\) 数组可以通过线性筛筛出来。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int Maxn=1e6+10;
const int mod=998244353;
int n;
int a[Maxn];
ll dp[Maxn];
ll sum[Maxn];
bool is_prime[Maxn];
vector<int>prime;
int f[Maxn];
int mu[Maxn];
void init()
{
for(int i=2;i<=(int)1e6;i++) is_prime[i]=1;
for(int i=2;i<=(int)1e6;i++)
{
if(is_prime[i])
{
mu[i]=1;
prime.push_back(i);
}
for(int j=0;j<prime.size() && i*prime[j]<=(int)1e6;j++)
{
is_prime[i*prime[j]]=0;
mu[i*prime[j]]=-mu[i];
f[i*prime[j]]=prime[j];
if(i%prime[j]==0)
{
mu[i*prime[j]]=0;
break;
}
}
}
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
init(); dp[1]=1; sum[1]=1;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)
{
for(int j=1;j*j<=a[i];j++)
{
if(a[i]%j) continue;
dp[i]+=mu[j]*sum[j];
if(j*j!=a[i]) dp[i]+=mu[a[i]/j]*sum[a[i]/j];
dp[i]=(dp[i]%mod+mod)%mod;
}
for(int j=1;j*j<=a[i];j++)
{
if(a[i]%j) continue;
sum[j]+=dp[i]; if(sum[j]>=mod) sum[j]-=mod;
if(j*j!=a[i]) sum[a[i]/j]+=dp[i]; if(sum[a[i]/j]>=mod) sum[a[i]/j]-=mod;
}
}
cout<<dp[n]<<endl;
return 0;
}

浙公网安备 33010602011771号