题意:哈斯图是有限偏序集的一种表示形式,对于任意正整数n,n的因子根据它们的可除性自然地形成了一个哈斯图。记\(H_n\)为n的因子形成的哈斯图,对于任意两个因子u,v,哈斯图中边\((u,v)\)存在当且仅当存在一个质数\(p\),使得\(u=pv\)
给定一个正整数n,求\(H_1,H_2,\cdots,H_n\)中的边数和.
设\(H(i)\)的边数为\(f(i)\)
那么若i为质数的幂,即\(i=p^e\)时,\(f(i)=f(p^e)=e\)
否则,假设\(i=\prod p^e\)枚举\(i\)的质因数及其次数,得
其中\(d(x)\)表示\(x\)的因数个数。
这个式子的含义是,\(f(i/p^e)\)中的任意一条边\((u,v)\)的两端可以乘上\(p\)的\(1\)到\(e\)次幂,可以得到e条新边,再算上\(f(i/p^e)\),就是\((e+1)f(i/p^e)\)。并且\(f(i/p^e)\)的每个因子\(x\),都可以贡献\(e\)条形如\((p^kx,p^{k-1}x)\)的边。
\(f(x)\)和\(d(x)\)均符合“在质数的次幂处可以快速求值“这一条件,并且\(d(x)\)是完全积性函数。但\(f(x)\)没有积性,不好搞。
由\(f(p) = 1\),可以取\(f(x)=1\)求其在质数处的前缀和(质数个数)(min_25的第一部分)
再由上面那个递推式可设\(S(i, j)\)为\([2,i]\)中,满足\(x\)的最小质因子比第\(j\)个质数大的\(f(x)\)的和。那么最终所求的答案即为\(S(n,0)\)
min_25的第二部分的递推式为
这是在
的情况下推出的。
而本题中应基于上述的递推式改为
其中\(d(p_k^e)=(e+1)\),\(S_d\)仍然和正常的min_25一致。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 7;
#define ll long long
const ll md = 1145140019;
bool isp[maxn];
int tot, totw;
ll n, pri[maxn], idx1[maxn], idx2[maxn], T, w[maxn], g[maxn], sqrtn;
ll ksm(ll a, ll b) {
ll res = 1;
while (b) {
if (b & 1) res = res * a % md;
a = a * a % md;
b >>= 1;
}
return res;
}
void sieve() {
memset(isp, true, sizeof(isp));
isp[1] = false;
for (int i = 2; i <= 100000; i++) {
if (isp[i]) {
pri[++tot] = i;
}
for (int j = 1; j <= tot && i * pri[j] <= 100000; j++) {
isp[i*pri[j]] = 0;
if (i % pri[j] == 0) break;
}
}
}
typedef pair<ll, ll> pii;
pii S(ll i, ll j) {
if (pri[j] > i) return (pii){0,0};
int id = i > sqrtn ? idx2[n / i] : idx1[i];
ll sp = (g[id] - j + md) % md, d = 2ll * sp % md;
for (int k = j + 1; k <= tot && pri[k] * pri[k] <= i; k++) {
for (ll p = pri[k], e = 1; p <= i; p *= pri[k], e++) {
pii tmp = S(i / p, k);
d = (d + (tmp.second + (e > 1)) * (e + 1)/*d(pre[k]^e), 合数部分*/ % md) % md;
sp = (sp + (e+1)*tmp.first % md + e*(tmp.second + (e>1))%md)%md;
}
}
return (pii){sp, d};
}
int main() {
cin >> T;
sieve();
while (T--) {
cin >> n;
totw = 0;
sqrtn = sqrt(n);
for (ll l = 1, r; l <= n; l = r + 1) {
ll k = n / l;
r = n / k;
w[++totw] = k;
g[totw] = (k-1+md) % md;
if (k <= sqrtn) idx1[k] = totw;
else idx2[r] = totw;
}
for (int i = 1; i <= tot; i++) {
for (int j = 1; j <= totw && pri[i] * pri[i] <= w[j]; j++) {
ll k = w[j] / pri[i];
ll pre = k <= sqrtn ? idx1[k] : idx2[n/k];
g[j] = (g[j] - 1ll * (g[pre] - (i - 1) + md) % md + md) % md;
}
}
printf("%lld\n", S(n, 0).first);
}
}
注意空间开1e5会段错误,我也不知道为啥,,