P5518 [MtOI2019]幽灵乐团 / 莫比乌斯反演基础练习题 题解

怎么说呢,这题确实算是基础练习题。就是强行六合一确实很顶。

T 组询问,每次给出 A,B,C,对于 type=0,1,2,分别求:

i=1Aj=1Bk=1C(lcm(i,j)gcd(i,k))f(type)

其中:

f(0)=1,f(1)=ijk,f(2)=gcd(i,j,k)

答案对给定质数 p 取模。(1A,B,C105,107p1.05×109,T=70,p 是质数)

首先先简单化简一下所给式子:

=i=1Aj=1Bk=1C(ijgcd(i,j)gcd(i,k))f(type)=i=1Aj=1Bk=1C(ijgcd(i,j)gcd(i,k))f(type)

这样我们的问题就转化为了求下面两个式子的值:

i=1Aj=1Bk=1Cif(type)i=1Aj=1Bk=1Cgcd(i,j)f(type)

原式的其他部分都可以通过简单交换 A,BB,C 转化成上式。接下来我们就分别对 type=0,1,2 推一推式子。

type=0 是比较简单的一种情况。对于第一个式子我们有:

i=1Aj=1Bk=1Ci=(i=1Aj=1Bi)C=(i=1AiB)C=((A!)B)C

而对于第二个式子,我们考虑比较套路的枚举 gcd,下文中记 f(t)=d|tdμ(td)

i=1Aj=1Bk=1Cgcd(i,j)=(i=1Aj=1Bgcd(i,j))C=(d=1Adi=1Aj=1B[gcd(i,j)=d])C=(d=1Adi=1Adj=1Bd[gcd(i,j)=1])C=(d=1Adi=1Adj=1Bdp|i,p|jμ(p))C=(d=1A(dp=1Adμ(p))AdpBdp)C=(t=1Af(t)AtBt)C

现在可以用类似预处理狄利克雷卷积的方法在 O(nlogn) 的时间内预处理出 f 的前缀积,然后就可以关于 t 整除分块单次 O(nlogn) 求解。

综上,我们想处理 type=0 的情况,需要额外预处理的是 μff 的前缀积和阶乘。预处理时间复杂度 O(nlogn),单次询问 O(nlogn)。总式子:

((A!)B)C((B!)A)C(t=1Af(t)AtBt)C(t=1Af(t)AtCt)B

然后是 type=1,这种情况稍微复杂一点,但思维难度也不是很大。下文中记 S(n)=n(n+1)2,T(n)=i=1nii。首先对于第一个式子:

i=1Aj=1Bk=1Ciijk=(i=1Aj=1Biij)S(C)=((i=1Aii)S(B))S(C)=(T(A)S(B))S(C)

而对于第二个式子,还是套路枚举 gcd,只不过比 type=0 的情况化简繁琐了一点,记 g(t)=f(t)t2

i=1Aj=1Bk=1Cgcd(i,j)ijk=(i=1Aj=1Bgcd(i,j)ij)S(C)=(d=1Adi=1Aj=1B[gcd(i,j)=d]ij)S(C)=(d=1Adi=1Adj=1Bd[gcd(i,j)=1]ijd2)S(C)=(d=1Add2i=1Adj=1Bdijp|i,p|jμ(p))S(C)=(d=1A(dp=1Adμ(p))(dp)2S(Adp)S(Bdp))S(C)=(t=1Ag(t)S(At)S(Bt))S(C)

预处理出 f 就可以求出 gg 的前缀积了,预处理 O(n),然后关于 t 数论分块即可做到单次 O(nlogn2) 求解。

综上,我们想求解 type=1 的情况,需要额外预处理的是 gg 的前缀积,T(n),预处理时间复杂度 O(n) 单次询问 O(nlogn2)。总式子:

(T(A)S(B))S(C)(T(B)S(A))S(C)(t=1Ag(t)S(At)S(Bt))S(C)(t=1Ag(t)S(At)S(Ct))S(B)

最后是 type=2 的情况,这玩意会比上面的都复杂,而且需要一定的思维能力。对于第一个式子,我们之前处理 k 的时候都是直接把它提到指数变成求和,然后发现非常好处理,但这次好像就不太行了。不过看到 gcd 我们还是先尝试枚举一下 gcd

i=1Aj=1Bk=1Cigcd(i,j,k)=(d=1Ci=1Aj=1Bi)dk=1C[gcd(i,j,k)=d]

好像也算把 k 变成求和扔到指数了,那就顺着这个思路继续推吧:

=(d=1Ci=1Adj=1Bdid)dk=1Cd[gcd(i,j,k)=1]=(d=1Ci=1Adj=1Bdid)dk=1Cdp|i,p|j,p|kμ(p)=(d=1Ap=1Cdi=1Adpj=1Bdpidp)dμ(p)Cdp=(t=1Ai=1Atj=1Btit)Ctd|tdμ(td)=(t=1AtAtBt(At!)Bt)Ctφ(t)=(t=1AtAtBtCtφ(t))(t=1A(At!)BtCtφ(t))

总算推完了,其中用到了 μid=φ 的结论。最后一步把整个式子拆开成两个部分是伏笔,会在第二个式子的化简过程中回收。第二个式子就更麻烦了,我们面前有两个 gcd,该枚举哪个呢?我会两个都试一遍,这里省略尝试的过程,结论是枚举 gcd(i,j,k) 是走得通的,大概是因为这个还是可以把 k 变成求和扔到指数:

i=1Aj=1Bk=1Cgcd(i,j)gcd(i,j,k)=(d=1Ci=1Aj=1Bgcd(i,j))dk=1C[gcd(i,j,k)=d]=(d=1Ci=1Adj=1Bdgcd(i,j)d)dk=1Cd[gcd(i,j,k)=1]=(d=1Cp=1Cdi=1Adpj=1Bdpgcd(i,j)dp)dμ(p)Cdp

啊,到这里这个式子就稍微有点复杂了,再强行往下化就有点麻烦了。我们考虑对于 dp,gcd(i,j) 我们有完全两种化简思路,所以考虑把这俩拆开做。先处理 dp

(d=1Cp=1Cdi=1Adpj=1Bdpdp)dμ(p)Cdp=(t=1CtAtBt)φ(t)Ct=t=1AtAtBtCtφ(t)

好像有点熟悉?是的,这东西在分子上也出现了,而且分子分母上都出现了两次,可以约掉了!然后我们考虑 gcd(i,j),思路还是枚举 gcd

(d=1Cp=1Cdi=1Adpj=1Bdpgcd(i,j))dμ(p)Cdp=t=1C(i=1Atj=1Btgcd(i,j))φ(t)Ct=t=1C(d=1Atdi=1Atj=1Bt[gcd(i,j)=d])φ(t)Ct=t=1C(d=1Atdi=1Atdj=1Btdp|i,p|jμ(p))φ(t)Ct=t=1C(d=1Atp=1Atddμ(p)AtdpBtdp)φ(t)Ct=t=1C(T=1Atf(T)AtTBtT)φ(t)Ct

这里用到了这个结论:

abc=abc

然后就可以整除分块套整除分块解决了,时间复杂度 O(n0.75logn)。整除分块套整除分块的复杂度:

O(0n0.5x0.5dx)=O(n0.75)

综上,我们想要解决 type=2 的情况,需要额外预处理的是 φφ 的前缀积。预处理时间复杂度 O(n),单次询问时间复杂度 O(n0.75logn)。总式子:

(t=1A(At!)BtCtφ(t))(t=1A(Bt!)AtCtφ(t))t=1C(T=1Atf(T)AtTBtT)φ(t)Ctt=1B(T=1Atf(T)AtTCtT)φ(t)Bt

总结一下吧。我们要预处理的东西是 μff 的前缀积,gg 的前缀和,T(n),阶乘,φφ 的前缀积。

总时间复杂度 O(nlogn+Tn0.75logn)。注意在指数上的东西要对 p1 取模。

#include <cstdio>
#include <algorithm>
const int N = 1e5 + 10; typedef long long ll; int mod;
int f[N], sf[N], s[N], isf[N], mu[N], fT[N], f2[N], sf2[N], isf2[N], phi[N], sp[N], p[N], vis[N], fac[N], tp;
inline int ksm(int a, int b)
{
    if (b < 0) return ksm(ksm(a, -b), mod - 2);
    int ret = 1; 
    while (b)
    {
        if (b & 1) ret = (ll)ret * a % mod;
        a = (ll)a * a % mod; b >>= 1;
    }
    return ret;
}
inline void init(int n)
{
    mu[1] = phi[1] = fT[0] = fac[0] = sf[0] = isf[0] = sf2[0] = isf2[0] = 1;
    for (int i = 2; i <= n; ++i)
    {
        if (!vis[i]) p[++tp] = i, mu[i] = -1, phi[i] = i - 1;
        for (int j = 1; j <= tp && i * p[j] <= n; ++j)
        {
            vis[i * p[j]] = 1;
            if (i % p[j] == 0) { mu[i * p[j]] = 0; phi[i * p[j]] = (ll)phi[i] * p[j] % (mod - 1); break; }
            mu[i * p[j]] = -mu[i]; phi[i * p[j]] = (ll)phi[i] * phi[p[j]] % (mod - 1); 
        }
    }
    for (int i = 1; i <= n; ++i) f[i] = f2[i] = 1;
    for (int i = 1; i <= n; ++i)
        for (int j = i; j <= n; j += i) f[j] = (ll)f[j] * ksm(i, mu[j / i]) % mod;
    for (int i = 1; i <= n; ++i) f2[i] = ksm(f[i], (ll)i * i % (mod - 1));
    for (int i = 1; i <= n; ++i) fT[i] = (ll)fT[i - 1] * ksm(i, i) % mod;
    for (int i = 1; i <= n; ++i) sp[i] = (sp[i - 1] + phi[i]) % (mod - 1);
    for (int i = 1; i <= n; ++i) fac[i] = (ll)fac[i - 1] * i % mod;
    for (int i = 1; i <= n; ++i) sf[i] = (ll)sf[i - 1] * f[i] % mod, sf2[i] = (ll)sf2[i - 1] * f2[i] % mod;
    isf[n] = ksm(sf[n], mod - 2); isf2[n] = ksm(sf2[n], mod - 2);
    for (int i = n - 1; i >= 1; --i) isf[i] = (ll)isf[i + 1] * f[i + 1] % mod, isf2[i] = (ll)isf2[i + 1] * f2[i + 1] % mod;
    for (int i = 1; i <= n; ++i) s[i] = (ll)i * (i + 1) / 2 % (mod - 1);
}
inline int type0(int A, int B, int C)
{
    int ans1, ans2;
    ans1 = (ll)ksm(fac[A], (ll)B * C % (mod - 1)) * ksm(fac[B], (ll)A * C % (mod - 1)) % mod;
    int n = std::min(A, B), t1 = 1, t2 = 1;
    for (int l = 1, r; l <= n; l = r + 1)
    {
        r = std::min(A / (A / l), B / (B / l));
        t1 = (ll)t1 * ksm((ll)sf[r] * isf[l - 1] % mod, (ll)(A / l) * (B / l) % (mod - 1)) % mod;
    }
    t1 = ksm(t1, C); n = std::min(A, C);
    for (int l = 1, r; l <= n; l = r + 1)
    {
        r = std::min(A / (A / l), C / (C / l));
        t2 = (ll)t2 * ksm((ll)sf[r] * isf[l - 1] % mod, (ll)(A / l) * (C / l) % (mod - 1)) % mod;
    }
    t2 = ksm(t2, B); ans2 = (ll)t1 * t2 % mod;
    return (ll)ans1 * ksm(ans2, mod - 2) % mod;
}
inline int type1(int A, int B, int C)
{
    int ans1, ans2;
    ans1 = (ll)ksm(fT[A], (ll)s[B] * s[C] % (mod - 1)) * ksm(fT[B], (ll)s[A] * s[C] % (mod - 1)) % mod;
    int n = std::min(A, B), t1 = 1, t2 = 1;
    for (int l = 1, r; l <= n; l = r + 1)
    {
        r = std::min(A / (A / l), B / (B / l));
        t1 = (ll)t1 * ksm((ll)sf2[r] * isf2[l - 1] % mod, (ll)s[A / l] * s[B / l] % (mod - 1)) % mod;
    }
    t1 = ksm(t1, s[C]); n = std::min(A, C);
    for (int l = 1, r; l <= n; l = r + 1)
    {
        r = std::min(A / (A / l), C / (C / l));
        t2 = (ll)t2 * ksm((ll)sf2[r] * isf2[l - 1] % mod, (ll)s[A / l] * s[C / l] % (mod - 1)) % mod;
     }
     t2 = ksm(t2, s[B]); ans2 = (ll)t1 * t2 % mod;
     return (ll)ans1 * ksm(ans2, mod - 2) % mod;
}
inline int type2(int A, int B, int C)
{
    int ans1, ans2;
    int n = std::min(A, std::min(B, C)), m, t1 = 1, t2 = 1;
    for (int l = 1, r; l <= n; l = r + 1)
    {
        r = std::min(A / (A / l), std::min(B / (B / l), C / (C / l)));
        t1 = (ll)t1 * ksm(fac[A / l], (ll)(B / l) * (C / l) % (mod - 1) * (sp[r] - sp[l - 1] + mod - 1) % (mod - 1)) % mod;
    }
    for (int l = 1, r; l <= n; l = r + 1)
    {
        r = std::min(A / (A / l), std::min(B / (B / l), C / (C / l)));
        t2 = (ll)t2 * ksm(fac[B / l], (ll)(A / l) * (C / l) % (mod - 1) * (sp[r] - sp[l - 1] + mod - 1) % (mod - 1)) % mod; 
    }
    ans1 = (ll)t1 * t2 % mod; t1 = t2 = 1; int mul, tA, tB, tC;
    for (int l = 1, r; l <= n; l = r + 1)
    {
        r = std::min(A / (A / l), std::min(B / (B / l), C / (C / l)));
        m = std::min(A / l, B / l); mul = 1; tA = A / l; tB = B / l;
        for (int i = 1, j; i <= m; i = j + 1)
        {
            j = std::min(tA / (tA / i), tB / (tB / i));
            mul = (ll)mul * ksm((ll)sf[j] * isf[i - 1] % mod, (ll)(tA / i) * (tB / i) % (mod - 1)) % mod;
        }
        t1 = (ll)t1 * ksm(mul, (ll)(C / l) * (sp[r] - sp[l - 1] + mod - 1) % (mod - 1)) % mod;
    }
    for (int l = 1, r; l <= n; l = r + 1)
    {
        r = std::min(A / (A / l), std::min(B / (B / l), C / (C / l)));
        m = std::min(A / l, C / l); mul = 1; tA = A / l; tC = C / l;
        for (int i = 1, j; i <= m; i = j + 1)
        {
            j = std::min(tA / (tA / i), tC / (tC / i));
            mul = (ll)mul * ksm((ll)sf[j] * isf[i - 1] % mod, (ll)(tA / i) * (tC / i) % (mod - 1)) % mod;
        }
        t2 = (ll)t2 * ksm(mul, (ll)(B / l) * (sp[r] - sp[l - 1] + mod - 1) % (mod - 1)) % mod;
    }
    ans2 = (ll)t1 * t2 % mod; 
    return (ll)ans1 * ksm(ans2, mod - 2) % mod;
}
int main()
{
    int T; scanf("%d%d", &T, &mod); init(N - 1);
    while (T--)
    {
        int A, B, C; scanf("%d%d%d", &A, &B, &C);
        printf("%d %d %d\n", type0(A, B, C), type1(A, B, C), type2(A, B, C));
    }
    return 0;
}
posted @   zhiyangfan  阅读(92)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示