题目链接
题解
一开始推的是(f(x)=sumlimits_{d|x}{g(d)}),其中(g(x))代表本质不同的质因数的个数,后来发现这个函数没有积性,无从下手。
后来看了题解,由于(n)可以表示成(prodlimits{p^c}),对于某个质因子(p),可得递推式(f(n)=(c+1) cdot f(frac{n}{p^c})+ccdot d(frac{n}{p^c})),其中(d(x))代表(x)的约数个数。
证明:
对于(f(frac{n}{p^c}))中任意的边((u,v)),({(u,v),(pu,v),(p^2u,pv),...,(p^cu,p^{c-1}v)})属于(f(n));除此之外,对于(frac{n}{p^c})的任意约数(d),({(pd,d),(p^2d,pd),...,(p^cd,p^{c-1}d)})也属于(f(n))。
这个递推式乍看起来没啥用,但是它其实是符合min25允许的结构的。对于素数(p),有(f(p)=1),(f(p^c)=c),可以快速计算。
min25统计答案时,是通过枚举最小质因子的方式统计,相比之下递推式只是多加了一个约数个数而已,而且约数个数是积性函数,也能用min25计算。因此在计算(f(x))时同时计算(d(x))的中间结果,在统计答案时加上去即可。
原本我太死板,对min25理解太浅,以为只有符合条件的积性函数才可以用min25解决,现在看来只要可以拆分质因子计算贡献,就可能可以使用min25解决。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
const int M = 1145140019;
const int INF = 0x3f3f3f3f;
typedef long long ll;
typedef pair<ll, ll> PII;
#define endl '
'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
ll num[N], ans[N];
ll id1[N], id2[N];
ll pri[N];
ll m, n;
int cnt;
bool isnp[N];
inline ll qpow(ll a, ll b, ll m) {
ll res = 1;
while(b) {
if(b & 1) res = (res * a) % m;
a = (a * a) % m;
b = b >> 1;
}
return res;
}
void init() {
isnp[1] = 1;
pri[cnt++] = 1;
for(int i = 2; i < N; i++) {
if(!isnp[i]) {
pri[cnt++] = i;
for(int j = 2 * i; j < N; j += i)
isnp[j] = 1;
}
}
}
int ID(ll x) { // 相当于hashmap,用于保存根号n个结果
if(x <= m) return id1[x];
return id2[n / x];
}
void solve(ll n) { // part 1
int cur = 0;
int mx = 0;
m = 0;
while(m * m <= n) {
if(pri[mx] <= m) mx++;
m++;
}
ll i = 1;
while(i <= n) {
num[cur] = n / i;
ans[cur] = (num[cur] - 1) % M;
if(num[cur] <= m) id1[num[cur]] = cur;
else id2[n / num[cur]] = cur;
cur++;
i = n / (n / i) + 1;
}
for(int i = 1; i < mx; i++) {
for(int j = 0; j < cur && 1ll * pri[i] * pri[i] <= num[j]; j++) {
ans[j] -= (ans[ID(num[j] / pri[i])] - (i - 1)) % M;
ans[j] %= M;
}
}
}
PII F(ll n, int k) { // part 2
ll res = 0; // f(x)
ll d = 0; // 约数个数
for(int i = k; 1ll * pri[i] * pri[i] <= n; i++) {
ll p = pri[i];
int cnt = 1;
while(p * pri[i] <= n) {
auto par = F(n / p, i + 1);
d += ((cnt + 1) * par.first % M + (cnt + 2)) % M;
res += ((cnt + 1) * par.second % M + cnt * par.first % M + (cnt + 1)) % M;
res %= M;
d %= M;
p = p * pri[i];
cnt++;
}
}
res += ans[ID(n)] - ((k - 1) ? ans[ID(pri[k - 1])] : 0) % M;
d += 2 * (ans[ID(n)] - ((k - 1) ? ans[ID(pri[k - 1])] : 0)) % M;
return {d % M, res % M};
}
int main() {
IOS;
init();
int t;
cin >> t;
while(t--) {
cin >> n;
solve(n);
cout << (F(n, 1).second % M + M) % M << endl;
}
}