1. 题面梗概
中译中 :给一个序列 A A A ,对于每一个位置 k k k ,求出除去 A k A_k A k 之外剩下所有元素的最小公倍数(LCM)对 998244353 998244353 998244353 取模的结果。
2. 逻辑推导
直接暴力求 N − 1 N-1 N − 1 个数的 LCM 是 O ( N 2 log A ) O(N^2 \log A) O ( N 2 log A ) ,显然会炸。我们需要对 LCM 的本质切入分析。
2.1 LCM
根据算术基本定理,若 L C M ( A 1 , … , A n ) = ∏ p i e i LCM(A_{1},\dots,A_{n}) = \prod p_{i}^{e_{i}} L C M ( A 1 , … , A n ) = ∏ p i e i ,则
e i = max 1 ≤ j ≤ n ( v p i ( A j ) ) e_{i} = \max_{1 \leq j \leq n}(v_{pi}(A_{j}))
e i = 1 ≤ j ≤ n max ( v p i ( A j ))
其中 v p ( x ) v_{p}(x) v p ( x ) 表示 x x x 的质因子 p p p 的最高次幂。
那么其实 LCM 本质上就是在收集所有数中每个质因子的最高次幂。
随便举个例子 8 , 9 , 15 8, 9, 15 8 , 9 , 15 。
8 = 2 3 , 9 = 3 2 , 15 = 3 1 × 5 1 8 = 2^3, 9 = 3^2, 15 = 3^1 \times 5^1 8 = 2 3 , 9 = 3 2 , 15 = 3 1 × 5 1 ,所以 L C M ( 8 , 9 , 15 ) = 2 3 × 3 2 × 5 1 LCM(8, 9, 15) = 2^3 \times 3^2 \times 5^1 L C M ( 8 , 9 , 15 ) = 2 3 × 3 2 × 5 1 。
2.2 最大值和次大值
如果我们删掉这个 A k A_{k} A k ,LCM 什么情况下会发生改变呢?我们发现对于某个质因子 p p p 来说,其在 LCM 的幂次中只有一种情况会改变:
A k A_{k} A k 贡献了这个质因子 p p p 的最高次幂,而且全序列中只有它贡献了。
此时删掉这个 A k A_{k} A k 之后,p p p 的幂次就会下降到次大高次幂,其他情况保持不变。
2.3 逆元
这里还有一点细节,如果 A k A_{k} A k 确实影响了全局 LCM,那么我们只需要:
L C M p m a x 1 × p m a x 2 = L C M p m a x 1 − m a x 2 \frac{LCM}{p^{max1}} \times p^{max2} = \frac{LCM}{p^{max1 - max2}}
p ma x 1 L C M × p ma x 2 = p ma x 1 − ma x 2 L C M
但是这里涉及到模意义下的除法了,我们需要求 p m a x 1 − m a x 2 p^{max1 - max2} p ma x 1 − ma x 2 在模 998244353 998244353 998244353 的逆元,由于这个模数本身就是质数,根据费马小定理,我们直接对 p m a x 1 − m a x 2 p^{max1 - max2} p ma x 1 − ma x 2 求 m o d − 2 mod - 2 m o d − 2 次幂即可。
2.4 结论
这是个数论问题,考察对 LCM 本质的理解以及维护最大和次大的技巧。
3. 代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 #include <bits/stdc++.h> using namespace std;using ll = long long ;const int maxA = 1e7 + 5 ;const int maxn = 2e5 + 5 ;const int mod = 998244353 ; ll p[maxA]; ll max1[maxA], max2[maxA], cnt1[maxA]; vector< pair< ll, ll > > fac[maxn]; vector< ll > primes; ll a[maxn];int n;ll qpow ( ll a, ll b ) { ll res = 1 ; while ( b ) { if ( b & 1 ) res = (res * a) % mod; a = (a * a) % mod; b >>= 1 ; } return res; }void init ( int n ) { for ( int i = 2 ; i <= n; ++ i ) { if ( p[i] ) continue ; for ( int j = i; j <= n; j += i ) if ( !p[j] ) p[j] = i; } }void get_factor ( int id, int val ) { fac[id].clear ( ); while ( val > 1 ) { int prime = p[val]; int cnt = 0 ; while ( val % prime == 0 ) val /= prime, cnt ++; fac[id].push_back ({ prime, cnt }); if ( cnt > max1[prime] ) { max2[prime] = max1[prime]; max1[prime] = cnt; cnt1[prime] = 1 ; primes.push_back ( prime ); } else if ( cnt == max1[prime] ) cnt1[prime] ++; else if ( cnt > max2[prime] ) max2[prime] = cnt; } }void solve ( ) { cin >> n; primes.clear ( ); for ( int i = 1 ; i <= n; ++ i ) { cin >> a[i]; get_factor ( i, a[i] ); } sort ( primes.begin ( ), primes.end ( ) ); primes.erase ( unique ( primes.begin ( ), primes.end ( ) ), primes.end ( ) ); ll LCM = 1 ; for ( int p : primes ) { LCM = ( LCM * qpow ( p, max1[p] ) ) % mod; } for ( int i = 1 ; i <= n; ++ i ) { ll res = LCM; for ( auto [p, c] : fac[i] ) { if ( c == max1[p] && cnt1[p] == 1 ) { ll inv = qpow ( p, max1[p] - max2[p] ) % mod; res = res * qpow ( inv, mod - 2 ) % mod; } } cout << res << " " ; } cout << "\n" ; for ( int p : primes ) { max1[p] = max2[p] = cnt1[p] = 0 ; } }int main ( ) { ios::sync_with_stdio (0 ); cin.tie (0 ); cout.tie (0 ); init ( maxA - 1 ); int _t = 1 ; cin >> _t ; while ( _t -- ) solve ( ); return 0 ; }
4. 复盘