令f(n) = lcm((n, 0), (n, 1),..., (n, n)),
g(n) = lcm(1, 2,.., n)。
则有结论:f(n) = g(n + 1) / (n + 1)(证明过程参阅:http://arxiv.org/pdf/0906.2295v2.pdf)。
待求的是ans = f(n) % mod
= (g(n + 1) / (n + 1)) % mod
= (g(n + 1) % mod * Inverse(n + 1) % mod) % mod。
其中Inverse(n)表示n的关于mod的乘法逆元,即满足Inverse(n) * n % mod = 1。
由费尔马小定理,amod-1%mod = 1, 于是 a * amod -2 % mod = 1。
则Inverse(n) = amod-2 %mod(mod 为素数),逆元唯一。
这样用快速幂很快计算一个数的乘法逆元,下面需要计算g(n)。
有以下结论g(n) =
g(n - 1) * p (如果存在质数p和整数k,满足n = pk)(1),
g(n - 1) (否则)。
我们需要预处理出一个对于每个整数其可能对应的p的数组以快速计算g(n)。
所有的素数存在唯一的p等于其本身和k=1满足式(1)。
对于任一整数可以写成素数幂的乘积的形式,因此可以用当前素数往后更新。
http://acm.hdu.edu.cn/showproblem.php?pid=5407
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 using namespace std; 6 typedef __int64 LL; 7 8 const int maxn = 1e6 + 10; 9 const int mod = 1e9 + 7; 10 11 int n; 12 LL f[maxn], g[maxn]; 13 14 LL power(LL a, LL p){ 15 LL ans = 1; 16 a %= mod; 17 while(p){ 18 if(p & 1){ 19 ans = (ans * a) % mod; 20 } 21 p >>= 1; 22 a = (a * a) % mod; 23 } 24 return ans; 25 } 26 27 LL Inverse(LL num){ 28 return power(num, mod - 2); 29 } 30 31 void solve(){ 32 LL ans = f[n + 1] * Inverse(n + 1) % mod; 33 printf("%I64d ", ans); 34 } 35 36 bool check(int num){ 37 int d = g[num]; 38 while(num % d == 0 && num > 1) num /= d; 39 return num == 1; 40 } 41 42 void init(){ 43 for(int i = 1; i < maxn; i++) g[i] = i; 44 for(int i = 2; i < maxn; i++){ 45 if(g[i] == i){ 46 for(int j = i + i; j < maxn; j += i){ 47 g[j] = i; 48 } 49 } 50 } 51 f[0] = 1; 52 for(int i = 1; i < maxn; i++){ 53 if(check(i)) f[i] = f[i - 1] * g[i] % mod; 54 else f[i] = f[i - 1]; 55 } 56 } 57 58 int main(){ 59 init(); 60 int T; 61 scanf("%d", &T); 62 while(T--){ 63 scanf("%d", &n); 64 solve(); 65 } 66 return 0; 67 }