牛客多校6G - Hasse Diagram(min25)

题目链接

Hasse Diagram

题解

一开始推的是\(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;
    }
}
posted @ 2021-08-05 22:51  limil  阅读(235)  评论(0编辑  收藏  举报