【题解】洛谷 P3704 [SDOI2017]数字表格

前置

幂转和

\(\prod\limits_{i} g^{d_i}=g^{\sum\limits_{i}d_i}\)\(g\) 是常数,或者至少在这次 \(\prod\) 中不会变化

gcd 的套路卷积

\[\begin{aligned} \because[x==1]&=\sum\limits_{d|x}\mu(d)\\ \therefore[gcd(x, y)==1]&=\sum\limits_{d|gcd(x, y)}\mu(d) \\&=\sum\limits_{d}\mu(d)[d|x][d|y] \end{aligned} \]

此时常常可以将 \(\sum\limits_{d}\mu(d)\) 提到式子最前面,而 \([d|x][d|y]\) 常常可以与枚举 \(x, y\) 的上界进行抵消,就像这样:

\(\sum\limits_{x=1}^{n}[x|d]\mathbf{f}(x)=\sum\limits_{x=1}^{\lfloor\frac{n}{d}\rfloor}\mathbf{f}(xd)\)

而当 \(\mathbf{f}(x)=\mathbf{id_0}(x)=\mathbf{1}(x)=1\) 时,原式 \(=\lfloor\frac{n}{d}\rfloor\)

推公式

\[\begin{aligned} \prod\limits_{i=1}^{n}\prod\limits_{j=1}^{m}f_{gcd(i,j)} &=\prod\limits_{i=1}^{n}\prod\limits_{j=1}^{m}\prod\limits_{g=1}^{\min(i, j)}f_g^{[gcd(i, j)==g]} \\&=\prod\limits_{g=1}^{\min(n, m)}f_g^{\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[gcd(i, j)==g]} \\&=\prod\limits_{g=1}^{\min(n, m)}f_g^{\sum\limits_{i=1}^{\lfloor\frac{n}{g}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{g}\rfloor}[gcd(i, j)==1]} \\&=\prod\limits_{g=1}^{\min(n, m)}f_g^{\sum\limits_{i=1}^{\lfloor\frac{n}{g}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{g}\rfloor}\sum\limits_{d|gcd(i, j)}\mu(d)} \\&=\prod\limits_{g=1}^{\min(n, m)}f_g^{\sum\limits_{i=1}^{\lfloor\frac{n}{g}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{g}\rfloor}\sum\limits_{d}[d|i][d|j]\mu(d)} \\&=\prod\limits_{g=1}^{\min(n, m)}f_g^{\sum\limits_{d}\mu(d)\sum\limits_{i=1}^{\lfloor\frac{n}{g}\rfloor}[d|i]\sum\limits_{j=1}^{\lfloor\frac{m}{g}\rfloor}[d|j]} \\&=\prod\limits_{g=1}^{\min(n, m)}f_g^{\sum\limits_{d}\mu(d)\lfloor\frac{n}{gd}\rfloor\lfloor\frac{m}{gd}\rfloor} \\&=\prod\limits_{g=1}^{\min(n, m)}f_g^{\sum\limits_{g|T}\mu(\frac{T}{g})\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor}&(\texttt{代换 }T=gd) \\&=\prod\limits_{T}\prod\limits_{g|T}^{\min(n, m)}f_g^{\mu(\frac{T}{g})\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor} \end{aligned} \]

\(\mathbf{F}(T)=\prod\limits_{g|T}f_g^{\mu(\frac{T}{g})}\)

\(\therefore\texttt{ 原式}=\prod\limits_{T=1}^{\min(n, m)}\mathbf{F}(T)^{\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor}\)

\(\mathbf{F}(T)\) 进行 \(\Theta(n\log n)\) 预处理前缀积

其余部分用数论分块 \(\Theta(\sqrt{n})\) 计算

代码

const int N = 1e6 + 7, P = 1e9 + 7;

inline int64 fpow(int64 a, int64 b) {
    int64 ret = 1;
    while (b) {
        if (b & 1) ret = ret * a % P;
        a = a * a % P;
        b >>= 1;
    }
    return ret;
}
#define inv(x) fpow(x, P - 2)

namespace euler {
    bool IsntPrime[N];
    int prs[N], pcnt, mu[N];
    unsigned a[N];

    void init_prs(int n) {
        mu[1] = 1;
        for (int i = 2; i <= n; ++i) {
            if (!IsntPrime[i]) {
                prs[++pcnt] = i;
                mu[i] = -1;
            }
            for (int j = 1; j <= pcnt && prs[j] * i <= n; ++j) {
                IsntPrime[prs[j] * i] = true;
                if (i % prs[j] == 0) break;
                mu[prs[j] * i] = -mu[i];
            }
        }
    }
}

namespace fib {
    int f[N], invf[N];
    void init(int n) {
        invf[1] = f[1] = 1;
        for (int i = 2; i <= n; ++i) {
            f[i] = (f[i - 1] + f[i - 2]) % P;
            invf[i] = inv(f[i]);
        }
    }
}

namespace F {
    int64 f[N], sum[N];
    void init(int n) {
        for (int g = 0; g <= n; ++g) f[g] = 1;
        sum[0] = 1;
        for (int g = 1; g <= n; ++g) {
            for (int d = 1; d * g <= n; ++d) // T = d * g
                switch (euler::mu[d]) {
                    case 1: f[g * d] = f[g * d] * fib::f[g] % P; break;
                    case -1: f[g * d] = f[g * d] * fib::invf[g] % P; break;
                }
            sum[g] = sum[g - 1] * f[g] % P;
            // printf("f[%d] = %lld\n", g, f[g]);
        }
    }
    int64 prod(int l, int r) {
        return sum[r] * inv(sum[l - 1]) % P;
    }
}

signed main() {
    euler::init_prs(1e6);
    fib::init(1e6);
    F::init(1e6);
    int T, n, m;
    for (cin >> T; T--;) {
        cin >> n >> m;
        int64 ans = 1;
        for (int l = 1, r, bound = min(n, m); l <= bound; l = r + 1) {
            r = min(n / (n / l), m / (m / l));
            ans = ans * fpow(F::prod(l, r), int64(n / l) * (m / l) % (P - 1)) % P;
        }
        cout << ans << endl;
    }
    return 0;
}
posted @ 2021-03-15 21:58  航空信奥  阅读(63)  评论(0编辑  收藏  举报