题目链接:Luogu 5106

善良的 dkw 决定直接告诉你题面:

$$ \prod_{i_1 = 1} ^ n \prod_{i_2 = 1} ^ n \cdots\prod_{i_k = 1} ^ n \varphi(\operatorname{lcm}(i_1, i_2, \cdots, i_k)) $$

请你求上述式子,答案对 $10 ^ 9 + 7$ 取模。

其中 $\operatorname{lcm}(i_1, i_2, \cdots, i_k)$ 表示这 $k$ 个数的最小公倍数。特别地,一个数的 $\operatorname{lcm}$ 是自身

数据范围:$1\le n,k\le 10^6$。


Solution

由于欧拉函数是积性函数,于是我们对每个质因子分别求解。

考虑一个质因子的若干次幂的贡献。设当前考虑的质因子为 $p$,指数为 $c$,有 $m$ 个 $k$ 元组的 $\operatorname{lcm}$(假设为 $x$) 满足 $p^c\mid x$ 且 $p^{c+1}\not\mid x$(换言之,这 $k$ 个数中质因子 $p$ 指数的最大值为 $c$),则 $p^c$ 对答案的贡献为 $\varphi(p^c)^m$。

那么我们怎么求出这个 $m$ 呢?单单考虑一个 $p^c$ 比较麻烦,我们考虑容斥

  • 在 $1$ 到 $n$ 中,是 $p^1,p^2,\cdots,p^c$ 任意一者的倍数,但不是 $p^{c+1}$ 的倍数的有 $n - \left\lfloor\frac{n}{p^{c+1}}\right\rfloor$ 个。
  • 在 $1$ 到 $n$ 中,是 $p^1,p^2,\cdots,p^{c-1}$ 任意一者的倍数,但不是 $p^c$ 的倍数的有 $n - \left\lfloor\frac{n}{p^c}\right\rfloor$。

那么满足条件的 $k$ 元组,要求这 $k$ 个数都满足第一个条件,不都满足第二个条件。于是有 $m = \left(n - \left\lfloor\frac{n}{p^{c+1}}\right\rfloor\right) ^ k - \left(n - \left\lfloor\frac{n}{p^{c}}\right\rfloor\right) ^ k$。

分析一下复杂度。质数一共有 $\mathcal O\left(\frac{n}{\log n}\right)$ 个,枚举指数的复杂度为 $\mathcal O(\log n)$,快速幂的复杂度为 $\mathcal O(\log n)$。故总复杂度为 $\mathcal O(n\log n)$,可以通过本题。

时间复杂度:$\mathcal O(n\log n)$。


Code

#include <cstdio>

const int N = 1e6 + 5;
const int mod = 1e9 + 7, phiMod = 1e9 + 6;

int n, k, tot, p[N], phi[N];
bool flg[N];

void sieve(int n) {
    phi[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!flg[i]) {
            p[++tot] = i;
            phi[i] = i - 1;
        }
        for (int j = 1; j <= tot && i * p[j] <= n; j++) {
            flg[i * p[j]] = true;
            if (i % p[j] == 0) {
                phi[i * p[j]] = phi[i] * p[j];
                break;
            } else {
                phi[i * p[j]] = phi[i] * phi[p[j]];
            }
        }
    }
}
int sub(int x, int y, int mod) {
    return x < y ? x - y + mod : x - y;
}
int pow(int x, int p, int mod) {
    int ans = 1;
    for (; p; p >>= 1, x = 1LL * x * x % mod) {
        if (p & 1) ans = 1LL * ans * x % mod;
    }
    return ans;
}
int main() {
    scanf("%d%d", &n, &k);
    sieve(n);
    int ans = 1;
    for (int i = 1; i <= tot; i++) {
        for (long long j = p[i]; j <= n; j *= p[i]) {
            int cnt = sub(pow(n - n / j / p[i], k, phiMod), pow(n - n / j, k, phiMod), phiMod);
            ans = 1LL * ans * pow(phi[j], cnt, mod) % mod;
        }
    }
    printf("%d\n", ans);
    return 0;
}
最后修改:2019 年 06 月 28 日
如果觉得我的文章对你有用,请随意赞赏