Loading

【题解】P5348 密码解锁

莫反套路多合一,记了记了。

思路来源于仙人 command_block.

思路

莫比乌斯反演 而非 \(\mu\) 反演。

首先考虑到令需要求的数列为 \(a\)\(\mu(x) = \sum\limits_{x \mid i} a_i\).

\(G(x) = \sum\limits_{x \mid i} a_i\),根据倍数反演得:\(G(x) = \sum\limits_{x \mid i} \mu(x) \mu(\frac{i}{x})\).

根据套路,令 \(k = \frac{i}{x}\),考虑求 \(f(m)\)。有: \(f(m) = \sum\limits_{i = 1}^{\lfloor \frac{n}{m} \rfloor} \mu(i) \mu(im)\).

根据套路,\(\mu(xy) = \mu(x) \mu(y) [\gcd(x, y) = 1]\),于是原式等价于:\(\sum\limits_{i = 1}^{\lfloor \frac{n}{m} \rfloor} \mu^2(i) \mu(m) [\gcd(i, m) = 1]\).

\(G(n, m) = \sum\limits_{i = 1}^n \mu^2(i) [\gcd(i, m) = 1]\).

原式根据 \(\mu\) 反演得:\(\sum\limits_{i = 1}^n \mu^2(i) \sum\limits_{d \mid \gcd(i, m)} \mu(d)\).

等价于:\(\sum\limits_{i = 1}^n \mu^2(i) \sum\limits_{d \mid i, d \mid m} \mu(d)\).

交换求和顺序得:\(\sum\limits_{d = 1}^n \mu(d) \sum\limits_{d \mid i} \mu^2(i) = \sum\limits_{d = 1}^n \mu(d) \sum\limits_{i = 1}^{\lfloor \frac{n}{d} \rfloor} \mu^2(id)\).

根据 \(\mu^2(xy) = \mu^2(x) \mu^2(y) [\gcd(x, y) = 1]\) 得:\(\sum\limits_{d = 1}^n \mu^3(d) \sum\limits_{i = 1}^{\lfloor \frac{n}{d} \rfloor} \mu^2(i) [\gcd(i, d) = 1]\).

因为 \(G(\lfloor \frac{n}{d} \rfloor, d) = \sum\limits_{i = 1}^{\lfloor \frac{n}{d} \rfloor} \mu^2(i) [\gcd(i, d) = 1]\),所以原式等于 \(\sum\limits_{d = 1}^n \mu^3(d) G(\lfloor \frac{n}{d} \rfloor, d)\).

这个式子是可以递归算的,但是时间复杂度不明确。

边界是 \(G(n, 0) = 0, G(n, 1) = \sum\limits_{i = 1}^n \mu^2(i)\),其中 \(G(n, 1)\) 可以用 SP4168 的方式杜教筛求。

考虑加上一些优化:

  • 预处理大数的质因子集合,将枚举因数转化成枚举子集。

  • 考虑只处理 \(\mu(i) \neq 0\)\(i\).

时间复杂度需要积分算,可以认为在 \(O(n^{\frac{5}{9}})\) 左右,具体不太会证。

此题还有我校仙人学长 yww 的题解,orz yww

代码

#include <cstdio>
#include <cmath>
#include <vector>
#include <bitset>
#include <map>
using namespace std;

typedef long long ll;

const int sz = 5e5 + 5;
const int st_sz = 7e2 + 5;

int t, m;
int tot, lim = 5e5;
int mu[sz], sum[sz], st_num[st_sz];
ll n;
ll st_mu[st_sz];
vector<int> pr;
bitset<sz> vis;
map<int, int> mp;

void init()
{
    vis[1] = true, mu[1] = 1;
    for (int i = 2; i <= lim; i++)
    {
        if (!vis[i]) mu[i] = -1, pr.push_back(i);
        for (int j = 0; (j < pr.size()) && (i * pr[j] <= lim); j++)
        {
            vis[i * pr[j]] = true;
            if (i % pr[j] == 0) { mu[i * pr[j]] = 0; break; }
            else mu[i * pr[j]] = -mu[i];
        }
    }
    for (int i = 1; i <= lim; i++) sum[i] = sum[i - 1] + (int)(mu[i] != 0), mu[i] += mu[i - 1];
    st_mu[0] = st_num[0] = 1;
    for (int i = 1; i < (1 << 9); i++) st_mu[i] = (i & 1) ? -st_mu[i >> 1] : st_mu[i >> 1];
}

int getS(int n)
{
    if (n <= lim) return sum[n];
    if (mp.count(n)) return mp[n];
    int sum = 0;
    for (int l = 1, r; l <= n; l = r + 1)
    {
        if (n / (l * l) == 0) break;
        r = sqrt(n / (n / (l * l)));
        sum += n / (l * l) * (mu[r] - mu[l - 1]);
    }
    return mp[n] = sum;
}

ll getG(int n, int st)
{
    if (!n) return 0;
    ll ans = 0;
    for (int i = st; i; i = (i - 1) & st) ans += st_mu[i] * getG(n / st_num[i], i);
    ans += getS(n);
    return ans;
}

bool prework()
{
    int tmp = m, cnt = 0;
    tot = 0;
    for (int i = 2; i * i <= tmp; i++)
    {
        if (tmp % i == 0)
        {
            for (int j = 0; j < (1 << tot); j++) st_num[j | (1 << tot)] = st_num[j] * i;
            tot++, cnt = 0;
            while (tmp % i == 0) tmp /= i, cnt++;
            if (cnt > 1) return false;
        }
    }
    if (tmp > 1)
    {
        for (int i = 0; i < (1 << tot); i++) st_num[i | (1 << tot)] = st_num[i] * tmp;
        tot++;
    }
    return true;
}

int main()
{
    init();
    scanf("%d", &t);
    while (t--)
    {
        scanf("%lld%d", &n, &m);
        n /= m;
        if (!prework()) { puts("0"); continue; }
        printf("%lld\n", st_mu[(1 << tot) - 1] * getG(n, (1 << tot) - 1));
    }
    return 0;
}
posted @ 2023-02-10 14:00  kymru  阅读(23)  评论(0编辑  收藏  举报