P3704:「SDOI2017」数字表格 – 题解

主要思路

先写出答案的式子:

\[ \prod_{i = 1}^n \prod_{i = 1}^m f(\gcd(i, j)) \]

老套路,枚举公约数:

\[ \prod_{d = 1}^{\min(n, m)} f(d)^{\sum_{i = 1}^n \sum_{j = 1}^m [gcd(i, j) = d]} \]

后面那个部分拿下来进行反演:

\[ \begin{aligned} \sum_{i = 1}^n \sum_{j = 1}^m [\gcd(i, j) = d] &= \sum_{i = 1}^{\lfloor \frac{n}{d} \rfloor} \sum_{j = 1}^{\lfloor \frac{m}{d} \rfloor} [\gcd(i, j) = 1] \\ &= \sum_{i = 1}^{\lfloor \frac{n}{d} \rfloor} \sum_{j = 1}^{\lfloor \frac{m}{d} \rfloor} \sum_{x|i, x|j} \mu(x) \\ &= \sum_{x = 1}^n \mu(x) \sum_{i = 1}^{\lfloor \frac{n}{d} \rfloor} \sum_{j = 1}^{\lfloor \frac{m}{d} \rfloor} [x|i] [x|j] \\ &= \sum_{x = 1}^n \mu(x) \sum_{i = 1}^{\lfloor \frac{n}{dx} \rfloor} \sum_{j = 1}^{\lfloor \frac{m}{dx} \rfloor} 1 \\ &= \sum_{x = 1}^n \mu(x) {\lfloor \frac{n}{dx} \rfloor} {\lfloor \frac{m}{dx} \rfloor} \\ &= \sum_{T = 1}^n {\lfloor \frac{n}{T} \rfloor} {\lfloor \frac{m}{T} \rfloor} \mu(\frac{T}{d}) \end{aligned} \]

放到原式:

\[ \begin{aligned} \prod_{d = 1} d^{\sum_{T = 1}^d {\lfloor \frac{n}{T} \rfloor} {\lfloor \frac{m}{T} \rfloor} \mu(\frac{T}{d})} &= \prod_{d = 1} \prod_{T = 1}^d d^{{\lfloor \frac{n}{T} \rfloor} {\lfloor \frac{m}{T} \rfloor} \mu(\frac{T}{d})} \\ &= \prod_{T = 1}^n (\prod_{d|T} d^{\mu(\frac{T}{d})})^{{\lfloor \frac{n}{T} \rfloor} {\lfloor \frac{m}{T} \rfloor}} \end{aligned} \]

我们可以考虑把\(\prod_{d|T} d^{\mu(\frac{T}{d})}\)作为整体来求,然后再整除分块来搞(当然前面那个部分要从前缀和改成前缀积)。且我们发现\(d\)一定是 Fibonacci 数列的一项,所以这个部分我们可以用埃筛的方式来进行求解:把贡献一个个乘上去。

代码

// P3704.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1e6 + 200, mod = 1e9 + 7;

int T, n, m, mu[MAX_N], primes[MAX_N], tot, f[MAX_N], g[MAX_N];
bool vis[MAX_N];

int quick_pow(int bas, int tim)
{
    int ret = 1;
    while (tim)
    {
        if (tim & 1)
            ret = 1LL * ret * bas % mod;
        bas = 1LL * bas * bas % mod;
        tim >>= 1;
    }
    return ret;
}

void init()
{
    mu[1] = 1;
    for (int i = 2; i < MAX_N; i++)
    {
        if (!vis[i])
            primes[++tot] = i, mu[i] = -1;
        for (int j = 1; j <= tot && 1LL * i * primes[j] < MAX_N; j++)
        {
            vis[i * primes[j]] = true;
            if (i % primes[j] == 0)
            {
                mu[i * primes[j]] = 0;
                break;
            }
            mu[i * primes[j]] = -mu[i];
        }
    }
    for (int i = 1; i < MAX_N; i++)
        f[i] = 1, g[i] = 1;
    int A = 1, B = 0;
    for (int i = 1; i < MAX_N; i++)
    {
        B = (A + B) % mod, A = (B - A + mod) % mod;
        int bucket[3] = {quick_pow(B, mod - 2), 1, B};
        for (int j = i, cnt = 1; j < MAX_N; j += i, cnt++)
        {
            f[j] = 1LL * f[j] * bucket[1 + mu[cnt]] % mod;
            g[j] = 1LL * g[j] * bucket[1 - mu[cnt]] % mod;
        }
    }
    f[0] = g[0] = 1;
    for (int i = 1; i < MAX_N; i++)
        f[i] = (1LL * f[i] * f[i - 1]) % mod, g[i] = (1LL * g[i] * g[i - 1]) % mod;
}

int main()
{
    scanf("%d", &T), init();
    while (T--)
    {
        scanf("%d%d", &n, &m);
        if (n > m)
            swap(n, m);
        int ans = 1;
        for (int x = 1, gx; x <= n; x = gx + 1)
        {
            gx = min(n / (n / x), m / (m / x));
            ans = 1LL * ans * quick_pow(1LL * f[gx] * g[x - 1] % mod, 1LL * (n / x) * (m / x) % (mod - 1)) % mod;
        }
        printf("%d\n", ans);
    }
    return 0;
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *