zoukankan      html  css  js  c++  java
  • 莫比乌斯反演

    前置知识:积性函数。具体可以看我的博客(积性函数专题) https://www.cnblogs.com/czyty114/p/12707134.html

    莫比乌斯反演

    (;)
    如果(F(n)=sum_{d|n} f(d))
    (f(n)=sum_{d|n} mu (d) F(frac{n}{d}))

    证明

    (;)
    看到网上很多人的证明都是推了一大堆的式子,本人表示瑟瑟发抖,这里推荐神葱的一种简单证明方法。

    [ecause F(n)=sum_{d|n} f(d) ]

    [ herefore F= f*I ]

    [ herefore F*mu=f*I*mu ]

    [ herefore F*mu=f*(I*mu)=f*epsilon ]

    显然,(f*epsilon=f)

    [ herefore F*mu=f ]

    [f(n)=sum_{d|n} mu (d) F(frac{n}{d}) ]

    (Q.E.D)
    (;)
    (;)

    几道经典的好题

    Problem 1

    (;)
    求:(sum_{i=1}^n sum_{j=1}^m gcd(i,j);;;;n,m leq 10^7)
    在含有多个(sum)(prod)这样的式子中,你要知道有几个基本套路(增加枚举量,交换枚举量),下面会一一说明
    啥都别说了,推式子吧。

    [sum_{i=1}^n sum_{j=1}^m gcd(i,j) ]

    [=sum_{i=1}^n sum_{j=1}^m id(gcd(i,j)) ]

    增加枚举量:

    [=sum_{i=1}^n sum_{j=1}^m sum_{d|gcd(i,j)} phi (d) ]

    交换枚举量:

    [=sum_{d=1}^{min(n,m)} sum_{i=1}^{lfloor frac{n}{d} floor} sum_{j=1}^{lfloor frac{m}{d} floor} phi (d) ]

    [=sum_{d=1}^{min(n,m)} phi(d) sum_{i=1}^{lfloor frac{n}{d} floor} sum_{j=1}^{lfloor frac{m}{d} floor} 1 ]

    [=sum_{d=1}^{min(n,m)} phi(d) lfloor frac{n}{d} floor lfloor frac{m}{d} floor ]

    所以我们直接预处理出(phi (1-min(n,m)))即可
    时间复杂度:(O(n))
    题目地址: https://www.luogu.com.cn/problem/U114004
    (;)
    (;)

    Problem 2

    (;)
    求:(sum_{i=1}^n sum_{j=1}^m lcm(i,j);;;;n,m leq 10^7)
    感觉和上道题差不多?其实要难些。
    推导:

    [sum_{i=1}^n sum_{j=1}^m lcm(i,j) ]

    根据(lcm(i,j)=frac{ij}{gcd(i,j)})

    [=sum_{i=1}^n sum_{j=1}^m frac{ij}{gcd(i,j)} ]

    枚举(gcd(i,j)=d)

    [=sum_{d=1}^{min(n,m)}sum_{i=1}^n sum_{j=1}^m [gcd(i,j)=d]frac{ij}{d} ]

    (d)提出来

    [=sum_{d=1}^{min(n,m)} frac{1}{d} sum_{i=1}^n sum_{j=1}^m [gcd(i,j)=d]ij ]

    [=sum_{d=1}^{min(n,m)} frac{1}{d} sum_{i=1}^{lfloor frac{n}{d} floor} sum_{j=1}^{lfloor frac{m}{d} floor} [gcd(i,j)=1]idjd ]

    [=sum_{d=1}^{min(n,m)} d sum_{i=1}^{lfloor frac{n}{d} floor} sum_{j=1}^{lfloor frac{m}{d} floor} [gcd(i,j)=1]ij ]

    我们把后面的式子挑出来。

    [S(n,m)=sum_{i=1}^{n} sum_{j=1}^{m} [gcd(i,j)=1]ij ]

    [=sum_{i=1}^{n} sum_{j=1}^{m} epsilon(gcd(i,j))ij ]

    根据(epsilon=mu * I)

    [=sum_{i=1}^{n} sum_{j=1}^{m} ij sum_{d|gcd(i,j)} mu (d) ]

    枚举(d)(交换枚举量)

    [=sum_{d=1}^{min(n,m)} sum_{i=1}^{lfloor frac{n}{d} floor} sum_{j=1}^{lfloor frac{m}{d} floor} mu(d) ij d^2 ]

    [=sum_{d=1}^{min(n,m)} mu(d) d^2 sum_{i=1}^{lfloor frac{n}{d} floor} sum_{j=1}^{lfloor frac{m}{d} floor} ij ]

    我们再把后面这个玩意提出来

    [h(n,m)=sum_{i=1}^n sum_{j=1}^m ij ]

    [=(1+2+cdots +n) imes (1+2+cdots +m) ]

    [=frac{n imes (n+1)}{2} imes frac{m imes (m+1)}{2} ]

    所以(h(n,m))可以(O(1))求解
    由此我们可以得到

    [S(n,m)=sum_{d=1}^{min(n,m)} mu(d) d^2 ;;h(lfloor frac{n}{d} floor,lfloor frac{m}{d} floor) ]

    [Ans=sum_{d=1}^{min(n,m)} d;;S(lfloor frac{n}{d} floor,lfloor frac{m}{d} floor) ]

    这样的复杂度是多少呢?
    首先我们枚举(d),然后计算(S(lfloor frac{n}{d} floor,lfloor frac{m}{d} floor))
    由于算(S(n,m))(O(min(n,m)))
    所以最终的复杂度大约是(O(frac{n}{1}+frac{n}{2}+cdots +frac{n}{n})=O(n;log;n))
    但是(10^7)貌似不太行啊。
    所以就要用到整除分块,用(O(sqrt n))分别计算:

    [S(n,m)=sum_{d=1}^{min(n,m)} mu(d) d^2 ;;h(lfloor frac{n}{d} floor,lfloor frac{m}{d} floor) ]

    [sum_{d=1}^{min(n,m)} d;;S(lfloor frac{n}{d} floor,lfloor frac{m}{d} floor) ]

    即可
    时间复杂度:(O(n))
    题目地址: https://www.luogu.com.cn/problem/P1829
    (Code:)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N=10000010,mod=20101009;
    int n,m,phi[N],mu[N],prime[N],cnt,res,st[N],sum[N];
    void qwq(int n)
    {
        phi[1]=1;mu[1]=1;
        for(int i=2;i<=n;i++)
        {
            if(!st[i])
    	{
    	    prime[++cnt]=i;
    	    phi[i]=i-1;
                mu[i]=-1; 
    	}
    	for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
    	{
    	    st[i*prime[j]]=1;
    	    if(i%prime[j])
    	    {
    	    	phi[i*prime[j]]=phi[i]*phi[prime[j]];
    	        mu[i*prime[j]]=mu[i]*mu[prime[j]];
    	    } 
    	    else
    	    {
    	        phi[i*prime[j]]=phi[i]*prime[j];
    		mu[i*prime[j]]=0;
    	        break;
    	    }
    	}
        }
    }
    int h(int n,int m)
    {
        return (1LL*n*(n+1)/2%mod)*(1LL*m*(m+1)/2%mod)%mod;
    }
    int S(int n,int m)
    {
        int ans=0;
        for(int l=1,r;l<=min(n,m);l=r+1)
        {
    	r=min(n/(n/l),m/(m/l));
    	ans=(ans+1LL*(sum[r]+mod-sum[l-1])%mod*h(n/l,m/l)%mod)%mod;
        }
        return ans;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        qwq(min(n,m));
        for(int d=1;d<=min(n,m);d++)
        {
    	sum[d]=(sum[d-1]+1LL*(mu[d]+mod)*d%mod*d%mod)%mod;
        }
        for(int l=1,r;l<=n;l=r+1)
        {
    	if(n/l==0||m/l==0)break;
    	r=min(n/(n/l),m/(m/l));
    	res=(res+1LL*(r-l+1)*(l+r)/2%mod*S(n/l,m/l)%mod)%mod;
        }
        printf("%d",res);
        return 0;
    } 
    

    (;)
    (;)

    Problem 3

    求:(1leq xleq n,1leq yleq m)(gcd(x,y))为质数的((x,y))有多少对
    (T)组询问。(1leq n,m leq 10^7,Tleq 10^4)
    即:

    [sum_{i=1}^n sum_{j=1}^m [gcd(i,j)=prime] ]

    推导:
    枚举prime

    [=sum_{k=1}^{cnt} sum_{i=1}^n sum_{j=1}^m [gcd(i,j)=prim_k] ]

    常规套路,消去一个(prim_k)

    [=sum_{k=1}^{cnt} sum_{i=1}^{lfloor frac{n}{prim_k} floor} sum_{j=1}^{lfloor frac{m}{prim_k} floor} [gcd(i,j)=1] ]

    [=sum_{k=1}^{cnt} sum_{i=1}^{lfloor frac{n}{prim_k} floor} sum_{j=1}^{lfloor frac{m}{prim_k} floor} epsilon(gcd(i,j)) ]

    [=sum_{k=1}^{cnt} sum_{i=1}^{lfloor frac{n}{prim_k} floor} sum_{j=1}^{lfloor frac{m}{prim_k} floor} sum_{d|gcd(i,j)} mu (d) ]

    [=sum_{d=1}^{min(lfloor frac{n}{prim_k} floor,lfloor frac{m}{prim_k} floor)}sum_{k=1}^{cnt} sum_{i=1}^{lfloor frac{lfloor frac{n}{prim_k} floor}{d} floor} sum_{j=1}^{lfloor frac{lfloor frac{m}{prim_k} floor}{d} floor} mu (d) ]

    (lfloor frac{lfloor frac{a}{b} floor}{c} floor=lfloor frac{a}{bc} floor)

    [=sum_{d=1}^{min(lfloor frac{n}{prim_k} floor,lfloor frac{m}{prim_k} floor)}sum_{k=1}^{cnt} sum_{i=1}^{lfloor frac{n}{prim_k d} floor} sum_{j=1}^{lfloor frac{m}{prim_k d} floor} mu (d) ]

    [=sum_{d=1}^{min(lfloor frac{n}{prim_k} floor,lfloor frac{m}{prim_k} floor)}sum_{k=1}^{cnt} lfloor frac{n}{prim_k d} floor imes lfloor frac{m}{prim_k d} floor imesmu (d) ]

    如果简单的枚举(prime),然后你就开心地发现你(TLE)了。
    有一个十分巧妙的方法:
    (T=prim_k d)
    然后我们枚举(T),即可得到下面的式子:

    [sum_{T=1}^{min(n,m)} lfloor frac{n}{T} floor imes lfloor frac{m}{T} floor imes sum_{prim_k|T} mu (frac{T}{prim_k}) ]

    然后你发现:(sum_{prim_k|T} mu (frac{T}{prim_k}))这个东西是可以预处理的。
    即:枚举每个质数的倍数,把(mu)加到倍数里面

    for(int i=1;i<=cnt;i++)
    {
        for(int j=1;prime[i]*j<=N-10;j++)
        {
            cur[prime[i]*j]+=mu[j];
        }
    }
    

    所以我们对于每组询问,用上述方法,再加上整除分块的优化,即可(O(sqrt n))求出答案
    时间复杂度:(O(T imes sqrt n))
    (Code:)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N=10000010;
    typedef long long LL;
    int n,m,T,prime[N],cnt,mu[N],st[N],sum[N],cur[N];
    int sumcur[N];
    void qwq(int n)
    {
    	mu[1]=1;
    	for(int i=2;i<=n;i++)
    	{
    		if(!st[i])
    		{
    			prime[++cnt]=i;
    			mu[i]=-1;
    		}
    		for(int j=1;j<=cnt&&i*prime[j]<=n;j++)
    		{
    			st[i*prime[j]]=1;
    			if(i%prime[j])
    			{
    				mu[i*prime[j]]=mu[i]*mu[prime[j]];
    			}
    			else
    			{
    				mu[i*prime[j]]=0;
    				break;
    			}
    		}
    	}
    	for(int i=1;i<=n;i++)sum[i]=sum[i-1]+mu[i];
    }
    LL Query(int n,int m)
    {
    	LL res=0;
    	for(int S=1,r;S<=min(n,m);S=r+1)
    	{
    		if(n/S==0||m/S==0)break;
    		r=min(n/(n/S),m/(m/S));
    		res=res+1LL*(n/S)*(m/S)*(sumcur[r]-sumcur[S-1]); 
    	}
    	return res;
    }
    int main()
    {
    	scanf("%d",&T);
    	qwq(N-10);
    	for(int i=1;i<=cnt;i++)
    	{
    		for(int j=1;prime[i]*j<=N-10;j++)
    		{
    			cur[prime[i]*j]+=mu[j];
    		}
    	}
    	for(int i=1;i<=N-10;i++)sumcur[i]=sumcur[i-1]+cur[i];
    	while(T--)
    	{
    		scanf("%d%d",&n,&m);
    		printf("%lld
    ",Query(n,m));
    	}
    	return 0;
    }
    

    (;)
    题目地址: https://www.luogu.com.cn/problem/P2257

  • 相关阅读:
    GIT更改clone方式 ;GIT的SSH配置
    关于web性能测试的一些总结
    pyinstaller 打包selenium程序后,消除chromdriver 控制台黑框
    pyinstaller 打包exe 遇到的坑
    jenkins 新增节点的3种方式
    class
    python 语法糖
    模块 subprocess
    模块 re
    模块 logging
  • 原文地址:https://www.cnblogs.com/czyty114/p/12720508.html
Copyright © 2011-2022 走看看