【Codeforces】CodeTON Round 3 D - Count GCD | 数学、容斥

211 阅读2分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

【Codeforces】CodeTON Round 3 D - Count GCD | 数学、容斥

题目链接

Problem - D - Codeforces

题目

image.png

题目大意

对于给定的含有 nn 个元素的数组 a1,a2,...,ana_1,a_2,...,a_n,构造数组 b1,b2,...,bnb_1,b_2,...,b_n,使得对于 1in1\le i\le n 的所有的 bib_i 均满足满足以下条件:

  • 1bim1\le b_i \le m
  • gcd(b1,b2,...,bi)=aigcd(b_1,b_2,...,b_i)=a_i

统计合法的 bb 数组的数量,对 998244353 取余。

思路

因为

gcd(b1,b2,...,bi)=aigcd(b1,b2,...,bi1)=ai1gcd(b_1,b_2,...,b_i)=a_i\\ gcd(b_1,b_2,...,b_{i-1})=a_{i-1}\\

所以 ai=gcd(ai1,bi)a_i=gcd(a_{i-1},b_i),先扫一遍是否存在 aiai1a_i\nmid a_{i-1},的情况,若存在则无解,否则统计在 [1,m][1,m] 中和 ai1a_{i-1} 的最大公约数为 aia_i 的个数。

又因为

gcd(a,b)=cgcd(ac,bc)=1gcd(a,b)=c\Leftrightarrow gcd(\frac{a}{c},\frac{b}{c})=1

所以我们只需要计算 [1,mai][1,\frac{m}{a_i}] 里和 ai1ai\frac{a_{i-1}}{a_i} 互质的数的个数。

怎样计算 [1,R][1,R] 里和 xx 互质的数的个数呢?这是一个经典问题,我们可以使用容斥。

[1,R][1,R]ww 的倍数的数量非常容易计算,显然为 Rw\lfloor\frac{R}{w}\rfloor

首先预处理出 xx 的质因数,先从全体数中减去 xx 的一个质因数的倍数的数量,但是 xx 两个质因数的倍数会被减去两次,所以我们需要把 xx 两个质因数的倍数的数量再加回来,同理需要把 xx 三个质因数的倍数的数量减去……

上述过程可以用状压 dp 进行处理。因为 aiai1a_i\mid a_{i-1} 所以单组数据我们需要处理的 xx 之积不超过 mm,操作次数非常少,可以通过此题。

代码

#include <bits/stdc++.h>
#define nnn printf("No\n")
#define yyy printf("Yes\n")
using namespace std;
using LL=long long;
typedef pair<int,int> PII;
const int N=500001;
//const LL mod=1000000007;
const LL mod=998244353;

int n,tot;
LL m,k;
LL gcd(LL x,LL y) {return y==0?x:gcd(y,x%y);}
LL a[N];
struct asdf{
	int x,y;
}ans[N];
LL p[N];
LL getans(LL x,LL r)// 1 到 r 中与 x 互质的数的个数 
{
	if (x==1) return r;
	tot=0;
	LL y=x;
	for (LL i=2;i*i<=x;++i)
		if (x%i==0)
		{
			p[++tot]=i;
			while (x%i==0) x/=i;
		}
	if (x>1) p[++tot]=x;
	LL res=0;
	for (int i=1;i<(1<<tot);++i)
	{
		LL cnt=-1;
		LL mult=1;
		for (int j=1;j<=tot;++j)
			if (i&(1<<j-1))
			{
				cnt*=-1;
				mult*=p[j];
			}
		mult=r/mult;
		res+=mult*cnt;
	} 
	if (res<0) return 0;
	if (r-res<0) return 0;
	return (r-res)%mod;
}
int solve()
{
	scanf("%d",&n);
	scanf("%lld",&m);
	for (int i=1;i<=n;++i) scanf("%lld",&a[i]);
	for (int i=2;i<=n;++i)
		if (a[i-1]%a[i]!=0) return printf("0\n"),0;
	LL ans=1;
	for (int i=2;i<=n;++i)
		ans=ans*getans(a[i-1]/a[i],m/a[i])%mod;
	printf("%lld\n",ans);
	return 0;
}
int main()
{
	int T=1;
	for (scanf("%d",&T);T--;) solve();
	return 0;
}