LiberOJ #124. 除数函数求和 【整除分块】
一、题目
二、分析
比较好的一题,首先我们要对题目和样例进行分析,明白题目的意思。
由于对于每一个$d$,它所能整除的数其实都是定的,且数量是$ \lfloor \frac{n}{d} \rfloor $ 最终推导出这个公式 $$ ans = \sum_{d=1}^{n} \lfloor \frac{n}{d} \rfloor d^{k}$$
对于$n <= 10^{7}$其实复杂度是可以接受的。但是对于求$d^{k}$这个复杂度如果直接用快速幂预处理肯定会T。
所以,这里用到了一个比较巧妙的方法,即联系线性求欧拉函数,因为幂次方是可以直接相乘的,所以把每个数相当于拆成了素数的$k$次方,这样我们原先求$n$次的快速幂,现在只需要求$\sqrt{n}$次快速幂,即所有素数求一次。然后线性筛一遍就可以了。
三、AC代码
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 #define ll long long 5 const int mod = 1e9 + 7; 6 const int maxn = 1e7 + 13; 7 int Sum[maxn], Prime[maxn], tot; 8 bool isPrime[maxn]; 9 int n, k; 10 11 int Pow(int a, int b) 12 { 13 int ans = 1; 14 while(b) 15 { 16 if(b & 1) 17 { 18 ans = 1ll * ans * a % mod; 19 } 20 b >>= 1; 21 a = 1ll * a * a % mod; 22 } 23 return ans; 24 } 25 26 void init() 27 { 28 tot = 0; 29 memset(isPrime, 0, sizeof(isPrime)); 30 isPrime[0] = isPrime[1] = 1; 31 Sum[1] = 1; 32 for(int i = 2; i < maxn; i++) 33 { 34 if(!isPrime[i]) 35 { 36 Prime[tot++] = i; 37 Sum[i] = Pow(i, k); 38 } 39 for(int j = 0; j < tot && 1ll * Prime[j] * i < maxn; j++) 40 { 41 isPrime[i * Prime[j]] = 1; 42 Sum[i * Prime[j]] = 1ll * Sum[i] * Sum[Prime[j]] % mod; 43 if(i % Prime[j]) 44 break; 45 } 46 } 47 } 48 49 int main() 50 { 51 int ans = 0; 52 cin >> n >> k; 53 init(); 54 for(int i = 1; i <= n; i++) 55 { 56 int t = n / i; 57 ans = (ans + 1ll * t * Sum[i] % mod) % mod; 58 } 59 cout << ans << endl; 60 return 0; 61 }