1 条题解

  • 0
    @ 2025-12-18 15:10:54

    G37 狄利克雷卷积

    G38 和式的变换

    G39 莫比乌斯反演

    问题

    给出 n,m,kn,m,k(1≤n,m,k≤1051 \leq n,m,k \leq 10^5),求$\sum\limits_{i=1}^n\sum\limits_{j=1}^m \lbrack \gcd(i,j)=k \rbrack$

    即:满足 1≤i≤n1 \leq i \leq n,1≤j≤m1 \leq j \leq m,且 gcd⁡(i,j)=k\gcd(i,j)=k 的二元组 (i,j)(i,j) 的数量。

    题解

    $\sum\limits_{i=1}^n\sum\limits_{j=1}^m\lbrack\gcd(i,j)=k\rbrack$
    $\sum\limits_{i=1}^{\frac{n}{k}}\sum\limits_{j=1}^{\frac{m}{k}}\lbrack\gcd(i,j)=1\rbrack$ 下来 nk,mk\frac{n}{k},\frac{m}{k} 替换为 n,mn,m
    $=\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{d |\gcd(i,j)}\mu(d)$ 注:[gcd⁡(i,j)=1]\lbrack\gcd(i,j)=1\rbrack 变成 ∑d∣gcd⁡(i,j)μ(d)\sum\limits_{d|\gcd(i,j)}\mu(d) 是 黄金变换操作。
    $\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{d=1}^n \lbrack d|i \rbrack \lbrack d|j \rbrack \mu(d)$ 注:∑d∣gcd⁡(i,j)μ(d)\sum\limits_{d|\gcd(i,j)}\mu(d) 变为 $\sum\limits_{d=1}^n \lbrack d|i \rbrack \lbrack d|j \rbrack \mu(d)$ 是银变换操作。
    $=\sum\limits_{d=1}^n \mu(d)\sum\limits_{i=1}^n\lbrack d|i \rbrack \sum\limits_{j=1}^m\lbrack d|j \rbrack$ 注:调换for循环的顺序
    $=\sum\limits_{d=1}^n\mu(d) \lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d}\rfloor$ 注: $\sum\limits_{i=1}^n\lbrack d|i \rbrack 变为 \lfloor \frac{n}{d} \rfloor$ 铜变换操作。

    到此,在 ∑d=1n\sum\limits_{d=1}^n 中 μ(d)\mu(d) 可以前缀和, $\lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d}\rfloor$ 可以分块加速即可,从而避免了 dd 逐个枚举 1…n1 \dots n。

    具体代码如下:

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    const int N=5e4;
    int pr, p[N+10];LL mu[N+10]; bool v[N+10];
    void init()
    {
    	memset(v,0,sizeof(v));
    	pr=0;mu[0]=0;mu[1]=1;
        for(int i=2;i<=N;i++)
    	{
            if(!v[i]) p[++pr]=i, mu[i]=-1; 
            for(int j=1;j<=pr&&p[j]*i<=N;j++)
    		{
                v[i*p[j]]=true;
                if(i%p[j]==0){mu[i*p[j]]=0;break;}
                mu[i*p[j]]=-mu[i];
            }
        }
        for(int i=1;i<=N;i++) mu[i]+=mu[i-1];
    }
    LL calc(int n, int m)
    {
        if(n>m)swap(n,m);
    	LL ans=0;
        for(int l=1,r;l<=n;l=r+1)
    	{
            r=min(n/(n/l), m/(m/l));
            ans+=(mu[r]-mu[l-1])*(n/l)*(m/l);
        }
        return ans;
    }
    int main()
    {
    	init();
    	int T,n,m,k;scanf("%d",&T);
    	while(T--)
    	{
            scanf("%d%d%d",&n,&m,&k);
    		n/=k;m/=k; 
            printf("%lld\n",calc(n,m));
    	}
        return 0;
    }
    
    • @ 2025-12-24 12:36:14

      第一步把n,m替换为n/k,m/k是指把i,j统一出k吗?

  • 1

信息

ID
2754
时间
30000ms
内存
64MiB
难度
6
标签
递交数
83
已通过
28
上传者