D. Count GCD

D. Count GCD

You are given two integers n and m and an array a of n integers. For each 1in it holds that 1aim.

Your task is to count the number of different arrays b of length n such that:

  • 1bim for each 1in, and
  • gcd(b1,b2,b3,...,bi)=ai for each 1in.

Here gcd(a1,a2,,ai) denotes the greatest common divisor (GCD) of integers a1,a2,,ai.

Since this number can be too large, print it modulo 998244353.

Input

Each test consist of multiple test cases. The first line contains a single integer t (1t100) — the number of test cases. The description of test cases follows.

The first line of each test case contains two integers n and m (1n2105, 1m109) — the length of the array a and the maximum possible value of the element.

The second line of each test case contains n integers a1,a2,,an (1aim) — the elements of the array a.

It is guaranteed that the sum of n across all test cases doesn't exceed 2105.

Output

For each test case, print a single integer — the number of different arrays satisfying the conditions above. Since this number can be large, print it modulo 998244353.

Example

input

复制代码
5
3 5
4 2 1
2 1
1 1
5 50
2 3 5 2 3
4 1000000000
60 30 1 1
2 1000000000
1000000000 2
复制代码

output

3
1
0
595458194
200000000

Note

In the first test case, the possible arrays b are:

  • [4,2,1];
  • [4,2,3];
  • [4,2,5].

In the second test case, the only array satisfying the demands is [1,1].

In the third test case, it can be proven no such array exists.

 

解题思路

  由于有gcd(b1,b2,b3,...,bi1)=ai1,因此容易发现ai=gcd(b1,b2,b3,...,bi1,bi)=gcd(ai1,bi)。因此如果aiai1,那么就一定无解,即满足条件的b数组数量为0

  否则,由于ai=gcd(ai1,bi),因此bi应该是ai的倍数,即bi=kai,其中1kmai。同时还需要注意到ai1,bi要满足gcd(ai1ai,biai)=1,即ai1bi除去最大公因子后,biai应该与ai1ai互质,否则最大公因子就不是ai了。因此选择的k还应该满足不能含有ai1ai的任何一个质因子,即满足gcd(k,ai1ai)=1。这个就可以用容斥原理来实现。

  因此需要先对ai1ai分解质因子,由于在不超过109的数中分解得到不同的质因子的个数最多不超过9个(2×3×5×7×11×13×17×19×23=223092870<109),因此为了实现方便这里容斥原理可以通过二进制枚举的方式来实现。

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 2e5 + 10, mod = 998244353;
 7 
 8 int a[N];
 9 
10 vector<int> divide(int x) {
11     vector<int> fs;
12     for (int i = 2; i <= x / i; i++) {
13         if (x % i == 0) {
14             fs.push_back(i);
15             while (x % i == 0) {
16                 x /= i;
17             }
18         }
19     }
20     if (x > 1) fs.push_back(x);
21     return fs;
22 }
23 
24 void solve() {
25     int n, m;
26     scanf("%d %d", &n, &m);
27     for (int i = 0; i < n; i++) {
28         scanf("%d", a + i);
29     }
30     LL ret = 1;
31     for (int i = 1; i < n; i++) {
32         if (a[i - 1] % a[i]) {    // a[i]不能整除a[i-1],无解 
33             ret = 0;
34             break;
35         }
36         else {
37             // a[i-1]/a[i]要与k互质 
38             vector<int> fs = divide(a[i - 1] / a[i]);    // 分解质因数 
39             LL k = m / a[i], s = 0;    // s是容斥原理的结果,即满足条件的k的个数 
40             for (int i = 0; i < 1 << fs.size(); i++) {
41                 int prod = 1, cnt = 0;    // prod是分母的乘积,cnt是乘了几个数 
42                 for (int j = 0; j < fs.size(); j++) {
43                     if (i >> j & 1) prod *= fs[j], cnt++;
44                 }
45                 // 根据容斥原理的公式,如果分母乘了奇数个数,那么符号是-号,否则是+号 
46                 if (cnt & 1) s -= k / prod;
47                 else s += k / prod;
48             }
49             ret = ret * s % mod;    // 乘法原理 
50         }
51     }
52     printf("%d\n", ret);
53 }
54 
55 int main() {
56     int t;
57     scanf("%d", &t);
58     while (t--) {
59         solve();
60     }
61     
62     return 0;
63 }
复制代码

  2024-02-28 更新:完善题解与代码。

  与上面的分析一样,每个 bi 能取的数的数量就是 1k (k=mai) 中与 ai1ai 互质的数的数量,最后根据乘法原理把每个 bi 能取的数的数量全部乘上就是答案。

  而 1k 中与 ai1ai 互质的数的数量,等价于先求出 1k 中与 ai1ai 不互质的数的数量,记作 s,再令 k 减去 s。假设 ai1ai 的不同质因子有 c 个分别是 p1,pc,由上面分析知道 c9。那么 s 就是 1k 中能被 p1,pc 中的至少一个数整除的数的数量。因此就可以用容斥原理来求出 s,参考能被整除的数

  另外由于 aiai1,因此每个 ai 都能整除 a1,意味着我们只需分解 a1 的质因子即可,其他 ai 的质因子必然被 a1 的包含。

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

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 2e5 + 10, mod = 998244353;

int a[N];

void solve() {
	int n, m;
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%d", a + i);
	}
	for (int i = 2; i <= n; i++) {
	    if (a[i - 1] % a[i]) {
	        printf("0\n");
	        return;
	    }
	}
	vector<int> p;
	int t = a[1];
	for (int i = 2; i * i <= t; i++) {
	    if (t % i == 0) {
	        p.push_back(i);
	        while (t % i == 0) {
	            t /= i;
	        }
	    }
	}
	if (t > 1) p.push_back(t);
	int ret = 1;
	for (int i = 2; i <= n; i++) {
	    int k = m / a[i], t = a[i - 1] / a[i];
	    vector<int> q;
	    for (auto &x : p) {
	        if (t % x == 0) q.push_back(x);
	    }
	    int s = 0;
	    for (int j = 1; j < 1 << q.size(); j++) {
	        int t = k, c = 0;
	        for (int k = 0; k < q.size(); k++) {
	            if (j >> k & 1) t /= q[k], c++;
	        }
	        if (c & 1) s = (s + t) % mod;
	        else s = (s - t) % mod;
	    }
	    ret = 1ll * ret * (k - s) % mod;
	}
	printf("%d\n", (ret + mod) % mod);
}

int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		solve();
	}
	
	return 0;
}

 

参考资料

  CodeTON Round 3 (Div. 1 + Div. 2) Editorial:https://codeforces.com/blog/entry/108504

  CodeTON Round 3 (Div. 1 + Div. 2, Rated, Prizes!) A - E:https://zhuanlan.zhihu.com/p/581011028

posted @   onlyblues  阅读(57)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示