🔗 UVA-12546 LCM Pair Sum

tags: 數學(Math)

範例程式碼已於UVA瘋狂程設(CPE) 上皆測試通過。(此題無 ZeroJudge )

題意

  • 給定 n=i=1mpiain=\prod_{i=1}^m p_i^{a_i},求:

f(n)=1xynlcm(x,y)=n(x+y)f(n)=\sum_{\substack{1 \leq x \leq y \leq n \\ \operatorname{lcm}(x, y)=n}}(x+y)

  • 由於答案可能很大,輸出對 109+710^9 + 7 取餘數的結果。

思路:乘法原理

雖然直覺上就是用乘法原理,但這裡我並沒有詳細證明從一個質因數推廣到多個質因數的過程。可以參考參考資料2.,也許能為你提供一點想法。

公式推導

  • 首先先忽略 xyx \leq y 的限制,考慮所有的 (x,y)(x, y) 對,由於 lcm(x,y)=n\operatorname{lcm}(x, y)=n ,所以除了 (n,n)(n, n) 以外的所有 (x,y)(x, y) 對都會被計算 22 次。
    • 換句話說,x=yx = y 的情況只會出現在 (n,n)(n, n) 這一對中,其他的 (x,y)(x, y) 對中都存在大小關係。
  • 再來先考慮只有一個質因數的情況,即令 n=piain=p_i^{a_i} ,考慮質因數 pip_ixx 中的次方數 kk
    1. 如果 pip_ixx 中的次方數 k<aik < a_i ,那麼 pip_iyy 中次數必須 =ai= a_i ,此時(x,y)=(pik,piai)(x, y) = (p_i^k, p_i^{a_i}) ,故此種情況下 xxf(n)f(n) 的貢獻為 k=0ai1pik\sum_{k=0}^{a_i-1} p_i^k
    2. 如果 pip_ixx 中的次方數 k=aik = a_i ,那麼 pip_iyy 中次數可以是 00aia_i ,共 ai+1a_i + 1 種情況,此時(x,y)=(piai,piky)(x, y) = (p_i^{a_i}, p_i^{k_y}) ,故此種情況下 xxf(n)f(n) 的貢獻為 (ai+1)piai(a_i + 1) \cdot p_i^{a_i}
  • 合併兩種情況,得出 xxf(n)f(n) 的貢獻為 j=0aipij+aipiai\sum_{j=0}^{a_i} p_i^j+a_i \cdot p_i^{a_i} 。同理,yyf(n)f(n) 的貢獻也是一樣的,故需乘 22 。 但由於除了 (n,n)(n, n) 以外的所有 (x,y)(x, y) 對都會被計算 22 次,所以最後答案為 (j=0aipij+aipiai)+n(\sum_{j=0}^{a_i} p_i^j+a_i \cdot p_i^{a_i}) + n 或是 (j=0ai1pij+(ai+1)piai)+n(\sum_{j=0}^{a_i-1} p_i^j+(a_i+1) \cdot p_i^{a_i}) + n
  • 最後利用乘法原理,擴展到有多個質因數的情況,即 n=i=1mpiain=\prod_{i=1}^m p_i^{a_i} ,答案為 i=1m(j=0aipij+aipiai)+n\prod_{i=1}^m (\sum_{j=0}^{a_i} p_i^j+a_i \cdot p_i^{a_i}) + n

實作過程

  • 雖然推出了公式,但直接寫肯定是會 TLE 的,我們不能傻傻的一項一項算。
  • 由於要從 ai0a_i^0 算到 aiaia_i^{a_i} ,每一項都會算到,因此我們可以保留前一項的結果,這樣就可以在 O(ai)O(a_i) 的時間內算出 j=0aipij+aipiai\sum_{j=0}^{a_i} p_i^j+a_i \cdot p_i^{a_i}
  • 此外,在計算過程中也要記得取餘數,避免數字過大。
  • 時間複雜度為:O(C×a)O(C \times a) ,其中 CC 為質因數個數,aa 為質因數中最大的次方數,對於每個詢問。
  • 空間複雜度為:O(C)O(C) ,輸入使用的空間 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MOD = 10**9 + 7
T = int(input())

for tc in range(1, T+1):
C = int(input())
PA = [list(map(int, input().split())) for _ in range(C)]

ans, n = 1, 1
for (p, a) in PA:
sigma, cur = 1, 1
for _ in range(1, a+1): # p^1 + p^2 + ... + p^a
cur = (cur * p) % MOD
sigma = (sigma + cur) % MOD
ans *= (sigma + a * cur) % MOD
n *= cur
ans = (ans + n) % MOD
print("Case %d: %d" % (tc, ans))
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
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int MOD = 1e9 + 7;
const int N = 20;

int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int T, C;
LL n, ans, sigma, cur;
LL P[N], A[N]; // p_i, a_i
cin >> T;
for (int t=1; t<=T; t++) {
cin >> C;
for (int i=0; i<C; i++) {
cin >> P[i] >> A[i];
}
ans = 1; n = 1;
for (int i=0; i<C; i++) {
sigma = 1; cur = 1;
for (int j=1; j<=A[i]; j++) {
cur *= P[i];
cur %= MOD;
sigma += cur;
sigma %= MOD;
}
sigma += A[i] * cur % MOD;
ans *= sigma % MOD;
ans %= MOD;
n *= cur;
n %= MOD;
}
ans = (ans + n) % MOD;
cout << "Case " << t << ": " << ans << endl;
}
return 0;
}

參考資料

  1. https://www.cnblogs.com/LLTYYC/p/11496596.html
  2. https://www.luogu.com.cn/article/z4xq670e
    • 其中的 getQ(1,ai,pi+1)\operatorname{getQ}(1, ai, pi + 1) 即為 j=0aipij\sum_{j=0}^{a_i} p_i^j

寫在最後

Cover photo is remixed from @SuiginToxic, thanks for their work!

個人認為這題是這次考試中最難的一題,但難點主要在公式推導,實作上並不困難。數學,我的一生之敵。