题解
提供一个贝尔级数爆拆 \(\sigma_0(n^2)\) 的做法(贝尔级数可见 command_block 的博客 Part. 2)。
设函数 \(f(n)=\sigma_0(n^2)\),不难证明这是一个积性函数:
对于 \(p\bot q\),\(f(p)f(q)=\sigma_0(p^2)\sigma_0(q^2)=\sigma_0((pq)^2)=f(pq)\),且 \(f(1)=1\)。
对于积性函数,就可以写出其贝尔级数:
不难看出是一个等差数列。
记 \(f'(x)\) 为积性函数 \(f(x)\) 的贝尔级数。
用生成函数基本操作:
众所周知,\(\mu^2(x)\) 的贝尔级数为 \(1+x\),\(1(x)\) 的贝尔级数为 \(\frac{1}{1-x}\)。
这里的 \(1(x)\) 指 \(\mu^{-1}(x)\),即 \((1*\mu)(x)=\epsilon(x)\),其中 \(*\) 表示迪利克雷卷积运算。
于是,\(f(x)=\mu^2*1*1\)。记 \(h(x)=\mu^2*1\):
对这个东西数论分块搞一搞,求 \(h(n)\) 的前缀和即可。
于是开始疯狂套用杜教筛:
快速求 \(\mu^2(n)\) 的前缀和即可(此步快速求 \(\mu^2(n)\) 前缀和可见此)。
关于时间复杂度:
预处理处 \(f(n)\)、\(h(n)\)、\(\mu^2(n)\)、\(\mu(n)\) 的前 \(n^{\frac{2}{3}}\) 项的前缀和,加上杜教筛即可,时间复杂度 \(\mathcal O(n^\frac{2}{3})\)。
关于 \(h(n)\) 的线性筛:
关于 \(f(n)\) 的线性筛:
关于 \(\sigma_0(p^k)\),显然有 \(\sigma_0(p^k)=k+1\)。
关于线性筛,在筛的时候若出现 i%prime[j] == 0
的情况,i * prime[j]
的最小质因数幂次必然大于等于 \(2\),可以省去上面 \(\sum\) 中的 \(\min\)。详见代码中 if(i%pr[j] == 0)
部分。
最后注意一下第一个点的时限为 1s,如果仍进行大规模预处理可能会 TLE。
参考实现
// Problem: SP20173 DIVCNT2 - Counting Divisors (square)
// From: Luogu
// URL: https://www.luogu.com.cn/problem/SP20173
// Time: 2022-03-16 19:39
// Author: lingfunny
#include <bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define LL long long
#define UL unsigned long long
using namespace std;
using namespace __gnu_pbds;
const int mxz = 7.7e7+100;
unsigned int mu2[mxz];
UL h[mxz], f[mxz];
int pr[(int)1e7], tot, mu[mxz], t, mxn;
LL ask[(int)1e7];
short flg[mxz];
inline int mi(int p, short c) {
int res = 1;
while(c) { if(c&1) res *= p; p *= p; c >>= 1; }
return res;
}
inline void Get() {
mu[1] = f[1] = h[1] = mu2[1] = 1;
for(int i = 2; i < mxn; ++i) {
if(!flg[i]) pr[++tot] = i, mu[i] = -1, flg[i] = 1, h[i] = 2, f[i] = 3;
for(int j = 1; j <= tot && pr[j] * i < mxn; ++j) {
int v = i * pr[j];
if(i%pr[j] == 0) {
flg[v] = flg[i] + 1;
// 最小质因数幂次至少为 2
h[v] = h[v/mi(pr[j], flg[v])]<<1;
f[v] = f[v/mi(pr[j], flg[v])] * (flg[v]+flg[v]+1);
break;
}
flg[v] = 1;
mu[v] = -mu[i];
h[v] = h[i] << 1;
f[v] = f[i] * 3;
}
}
for(int i = 1; i < mxn; ++i) {
mu2[i] = mu[i] * mu[i];
mu2[i] += mu2[i-1];
h[i] += h[i-1];
f[i] += f[i-1];
}
}
gp_hash_table <LL, UL> qh, qf, qm;
inline UL Qm(LL n) {
if(n < mxn) return mu2[n];
if(qm.find(n) != qm.end()) return qm[n];
UL res = 0;
for(int d = 1; (LL)d*d <= n; ++d)
res += mu[d] * (n/((LL)d*d));
qm[n] = res;
return res;
}
inline UL Qh(LL n) {
if(n < mxn) return h[n];
if(qh.find(n) != qh.end()) return qh[n];
register UL res = 0, pre = 0, cur = 0;
for(register LL l = 1, r; l <= n; l = r + 1) {
r = n/(n/l);
cur = Qm(r);
res += (cur-pre) * (n/l);
pre = cur;
}
qh[n] = res;
return res;
}
inline UL Qf(LL n) {
if(n < mxn) return f[n];
if(qf.find(n) != qf.end()) return qf[n];
register UL res = 0, pre = 0, cur = 0;
for(register LL l = 1, r; l <= n; l = r + 1) {
r = n/(n/l);
cur = Qh(r);
res += (cur-pre) * (n/l);
pre = cur;
}
qf[n] = res;
return res;
}
signed main() {
Get();
scanf("%d", &t);
LL n = 0;
for(int i = 1; i <= t; ++i) scanf("%lld", &ask[i]), n = max(n, ask[i]);
if(n <= 1e4+10) mxn = 1e4+2;
else mxn = mxz-100;
Get();
for(int i = 1; i <= t; ++i) printf("%llu\n", Qf(ask[i]));
return 0;
}