传送门.
设 s(n) s ( n ) 表示 nn的最小质因子 n n 的 最 小 质 因 子
先小反演一下:
∑ni=1∑nj=1sgcd(i,j)k ∑ i = 1 n ∑ j = 1 n s g c d ( i , j ) k
=∑nd=2s(d)k∗∑ni=1∑nj=1[gcd(i,j)=d] = ∑ d = 2 n s ( d ) k ∗ ∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = d ]
=∑nd=2s(d)k∗∑⌊nd⌋i=1∑⌊nd⌋j=1[gcd(i,j)=1] = ∑ d = 2 n s ( d ) k ∗ ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ n d ⌋ [ g c d ( i , j ) = 1 ]
=∑nd=2s(d)k∗(2(∑⌊nd⌋i=1ϕ(i))−1) = ∑ d = 2 n s ( d ) k ∗ ( 2 ( ∑ i = 1 ⌊ n d ⌋ ϕ ( i ) ) − 1 )
s s 的前缀和的筛法参考我的前一篇博客.
不过这里有个k次幂,于是初值就需要自然数幂和,模数 232 2 32 ,所以用第二类斯特林数,参考我的另一篇博客.
这个复杂度是 O(n−−√∗k2) O ( n ∗ k 2 ) 的,用线性筛法预处理一些前多少的幂和会快很多。
ϕ ϕ 的前缀和的话可以也用min_25筛,不过此时写杜教筛显然是最好的选择,因为快,且对 ⌊ni⌋ ⌊ n i ⌋ 已经有了标号,也不用写hash表。
Code:
#include
#include
#define ll long long
#define ul unsigned int
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;
const int N = 1e6 + 5;
int n, k, sqr, m;
int bz[N]; ul p[N], phi[N];
int w[N], i1[N], i2[N];
ul s[51][51], g[N], sk[N], sp[N], sum[N], h[N], c[N];
ul ksm(ul x, ul y) {
ul s = 1;
for(; y; y /= 2, x = x * x)
if(y & 1) s = s * x;
return s;
}
void sieve(int n) {
fo(i, 2, n) bz[i] = 0; p[0] = 0;
fo(i, 2, n) {
if(!bz[i]) p[++ p[0]] = i, phi[i] = i - 1, sk[p[0]] = ksm(i, k), sp[p[0]] = sp[p[0] - 1] + sk[p[0]], c[i] = ksm(i, k);
for(int j = 1; i * p[j] <= n; j ++) {
int k = i * p[j]; bz[k] = 1;
c[k] = c[i] * c[p[j]];
if(i % p[j] == 0) { phi[k] = phi[i] * p[j]; break;}
phi[k] = phi[i] * phi[p[j]];
}
}
phi[1] = c[1] = 1;
fo(i, 2, n) phi[i] += phi[i - 1], c[i] += c[i - 1];
}
ul zm(int n) {
if(n <= 1e6) return c[n];
ul S = 0;
fo(j, 1, k) {
ul p = 1;
fo(i, n - j + 1, n + 1)
p *= (i % (j + 1) == 0 ? i / (j + 1) : i);
S += s[k][j] * p;
}
return S;
}
int bx[N]; ul bs[N];
ul dg(int x) {
if(x <= 1e6) return phi[x];
int k = x <= sqr ? i1[x] : i2[n / x];
if(bx[k]) return bs[k];
bx[k] = 1; bs[k] = (ul) x * (x + 1) / 2;
for(int i = 2, j; i <= x; i = j + 1) {
j = x / (x / i);
bs[k] -= dg(x / i) * (j - i + 1);
}
return bs[k];
}
ul ans;
int main() {
scanf("%d %d", &n, &k); sqr = sqrt(n);
sieve(N - 5);
s[0][0] = 1; fo(i, 1, k) fo(j, 1, i) s[i][j] = s[i - 1][j - 1] + s[i - 1][j] * j;
for(int i = 1, j; i <= n; i = j + 1) {
j = n / (n / i); w[++ m] = n / i;
if(w[m] <= sqr) i1[w[m]] = m; else i2[n / w[m]] = m;
h[m] = w[m] - 1;
g[m] = zm(w[m]) - 1;
}
p[0] = 0; while(p[p[0] + 1] <= sqr) p[0] ++;
fo(j, 1, p[0]) for(int i = 1; i <= m && p[j] * p[j] <= w[i]; i ++) {
int k = (w[i] / p[j] <= sqr) ? i1[w[i] / p[j]] : i2[n / (w[i] / p[j])];
g[i] -= sk[j] * (g[k] - sp[j - 1]);
h[i] -= h[k] - j + 1;
sum[i] += g[k] - sp[j - 1];
}
fo(i, 1, m) sum[i] += h[i];
fo(i, 1, m) ans += (sum[i] - sum[i + 1]) * (dg(n / w[i]) * 2 - 1);
printf("%u", ans);
}