牛客多校6G - Hasse Diagram(min25)
题目链接
题解
一开始推的是\(f(x)=\sum\limits_{d|x}{g(d)}\),其中\(g(x)\)代表本质不同的质因数的个数,后来发现这个函数没有积性,无从下手。
后来看了题解,由于\(n\)可以表示成\(\prod\limits{p^c}\),对于某个质因子\(p\),可得递推式\(f(n)=(c+1) \cdot f(\frac{n}{p^c})+c\cdot 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 '\n'
#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;
}
}