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;
}
posted @ 2025-07-15 21:40  crazy--boy  阅读(7)  评论(0)    收藏  举报