• 莫比乌斯反演


    一些引理

    引理1
    (forall a,b,c in Z,leftlfloor frac{a}{bc} ight floor=leftlfloor frac{leftlfloor frac{a}{b} ight floor}{c} ight floor)
    证明:
    (frac{a}{b}=leftlfloor frac{a}{b} ight floor+r) ((0 leq r < 1))
    (leftlfloor frac{a}{bc} ight floor=leftlfloor frac{leftlfloor frac{a}{b} ight floor}{c} + frac{r}{c} ight floor=leftlfloor frac{leftlfloor frac{a}{b} ight floor}{c} ight floor)
    引理2
    (forall n in N_+,|{ leftlfloor frac{n}{d} ight floor | d in N_+ , d leq n }| leq leftlfloor 2sqrt{n} ight floor)
    证明: (leftlfloor frac{n}{d} ight floor) 的取值最多有 (leftlfloor 2sqrt{n} ight floor) 种。

    数论分块

    基本形式:
    (sum limits_{i=1}^n leftlfloor frac{n}{i} ight floor)
    对于每一个 (i) ,可以发现: (lfloor frac{n}{i} floor) 的每一种值呈块状分布。
    所以,对于任意一个 (i) ((i leq n)) ,需要找到一个最大的 (j) ((i leq j leq n)) ,使得 (leftlfloor frac{n}{i} ight floor=leftlfloor frac{n}{j} ight floor)
    此时,(j=leftlfloor frac{n}{leftlfloor frac{n}{i} ight floor} ight floor)
    考虑证明这一结论。
    首先,证明 (i leq j leq n)
    显然, (j leq n) ,所以,只需证明 (i leq j)
    (leftlfloor frac{n}{leftlfloor frac{n}{i} ight floor} ight floor geq leftlfloor frac{n}{frac{n}{i}} ight floor=leftlfloor i ight floor=i)
    所以 (i leq j)
    接下来,设 (k=leftlfloor frac{n}{i} ight floor) ,考虑证明 (j=leftlfloor frac{n}{k} ight floor) 为满足 (leftlfloor frac{n}{j} ight floor=k) 的最大值。
    (leftlfloor frac{n}{j} ight floor =k Rightarrow k leq frac{n}{j} <k+1 Rightarrow frac{1}{k+1} < frac{j}{n} leq frac{1}{k} Rightarrow frac{n}{k+1} <j leq frac{n}{k})
    又因为 (j) 是整数,所以 (j_{max}=leftlfloor frac{n}{k} ight floor)
    所以,每次以 ([i,j]) 为一块,直接算即可。
    例题
    (sum limits_{i=1}^n k mod i=sum limits_{i=1}^n k-i imes leftlfloor frac{k}{i} ight floor)
    直接数论分块算即可。

    ans=n*k;
    while(l<=n)
    {
    	l=r+1;
    	if(k/l) r=MIN(k/(k/l),n);
    	else r=n;
    	ans-=(k/l)*(l+r)*(r-l+1)/2;
    }
    

    二维数论分块
    考虑一个二维的版本。
    (sum limits_{i=1}^{min(n,m)} leftlfloor frac{n}{i} ight floor leftlfloor frac{m}{i} ight floor)
    把代码中 r=n/(n/l) 替换为 r=min(n/(n/l),m/(m/l)) 即可。

    狄利克雷卷积

    定义
    定义两个数论函数 (f,g) 的狄利克雷卷积为:
    ((f*g)(n)=sum limits_{d|n}f(d)g(frac{n}{d}))
    性质
    1.交换律:(f*g=g*f)
    2.结合律:((f*g)*h=f*(g*h))
    3.分配律:(f*(g+h)=f*g+f*h)
    4.(f*varepsilon=f)(varepsilon) 即狄利克雷卷积的单位元。(其中,(varepsilon (n)=[n=1])

    莫比乌斯函数

    定义
    (n=prod limits _{i=1}^k p_i^{c_i})(p_i) 是质数) ,
    (mu(n)= egin{cases} 1&n=1\ (-1)^k&c_i=1\ 0& ext{otherwise} end{cases})
    性质

    1. (sum_{d|n} mu(d)=varepsilon(n))
    2. ([gcd(i,j)=1] Longleftrightarrow sum limits_{d|gcd(i,j)} mu(d))

    莫比乌斯反演

    式子
    (f(n),g(n)) 为两个数论函数,则
    (f(n)=sum_{d|n} g(d) Rightarrow g(n)=sum_{d|n} mu(d) f(frac{n}{d}))
    (f(n)=sum_{n|d} g(d) Rightarrow g(n)=sum_{n|d} mu(frac{d}{n}) f(d))
    证明
    以第一个式子为例。
    (sum limits_{d|n} mu(d) f(frac{n}{d}) = sum limits_{d|n} mu(d) sum limits_{k|frac{n}{d}} g(k) = sum limits_{k|n} g(k) sum limits_{d|frac{n}{k}} mu(d) = sum limits_{k|n} g(k) [frac{n}{k}=1] = g(n))

    例题

    1
    (sum limits_{x=a}^b sum limits_{y=c}^d [gcd(x,y)]=k)
    容斥后,问题转化为求 (sum limits_{x=1}^n sum limits_{y=1}^m [gcd(x,y)]=k)
    同时除以 (k) ,得到 (sum limits_{x=1}^{leftlfloor frac{n}{k} ight floor} sum limits_{y=1}^{leftlfloor frac{m}{k} ight floor} [gcd(i,j)=1])
    根据前面莫比乌斯函数的性质,得到 (sum limits_{x=1}^{leftlfloor frac{n}{k} ight floor} sum limits_{y=1}^{leftlfloor frac{m}{k} ight floor} sum limits_{d|gcd(i,j)} mu(d))
    变换求和顺序, (sum limits_{d=1}^{min(n,m)} mu(d) sum limits_{x=1}^{leftlfloor frac{n}{k} ight floor} [d|x] sum limits_{y=1}^{leftlfloor frac{m}{k} ight floor} [d|y])
    (1) ~ (n)(d) 的倍数有 (leftlfloor frac{n}{d} ight floor) 个,得到 (sum limits_{d=1}^{min(n,m)} mu(d) leftlfloor frac{n}{kd} ight floor leftlfloor frac{m}{kd} ight floor)
    数论分块求解即可。

    #include<iostream>
    #include<cstdio>
    #define int long long
    using namespace std;
    const int N=5e4+10;
    int cnt,p[N],mu[N],f[N];
    int T,a,b,c,d,k;
    bool flag[N];
    int MIN(int x,int y)
    {
    	return x<y?x:y;
    }
    void O()
    {
    	mu[1]=1;
    	for(int i=2;i<N;i++)
    	{
    		if(!flag[i]) p[++cnt]=i,mu[i]=-1;
    		for(int j=1;j<=cnt&&i*p[j]<N;j++)
    		{
    			flag[i*p[j]]=1;
    			if(i%p[j]==0)
    			{
    				mu[i*p[j]]=0;
    				break;
    			}
    			mu[i*p[j]]=-mu[i];
    		}
    	}
    	for(int i=1;i<N;i++) f[i]=f[i-1]+mu[i];
    }
    int calc(int n,int m)
    {
    	int l=0,r=0,ans=0;
    	while(l<=MIN(n,m))
    	{
    		l=r+1;
    		if(n/l==0||m/l==0) r=MIN(n,m);
    		else r=MIN(n/(n/l),m/(m/l));
    		ans+=(n/l)*(m/l)*(f[r]-f[l-1]);
    	}
    	return ans;
    }
    signed main()
    {
    	O();
    	scanf("%lld",&T);
    	while(T--)
    	{
    		scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k);
    		printf("%lld
    ",calc(b/k,d/k)-calc((a-1)/k,d/k)-calc(b/k,(c-1)/k)+calc((a-1)/k,(c-1)/k));
    	}
    	return 0;
    }
    

    2
    (sum limits_{i=1}^n lcm(i,n))
    原式即 (sum limits_{i=1}^n frac{i imes n}{gcd(i,n)})
    将原式复制一遍,颠倒顺序,并且提出 (n) 一项,得 (frac{1}{2} imes (sum limits_{i=1}^{n-1} frac{i imes n}{gcd(i,n)}+sum limits_{i=n-1}^1 frac{i imes n}{gcd(i,n)})+n)
    根据 (gcd(i,n)=gcd(n-i,n)) ,得 (frac{1}{2} imes (sum limits_{i=1}^{n-1} frac{i imes n}{gcd(i,n)}+sum limits_{i=n-1}^1 frac{i imes n}{gcd(n-i,n)})+n)
    合并分母相同的项, (frac{1}{2} imes sum limits_{i=1}^{n-1} frac{n^2}{gcd(i,n)}+n)
    把一个 (n) 合回来, (frac{1}{2} imes sum limits_{i=1}^{n} frac{n^2}{gcd(i,n)}+ frac{n}{2})
    考虑枚举 gcd ,要统计 (gcd(i,n)=d) 的个数。
    (gcd(i,n)=d) 时, (gcd(frac{i}{d},frac{n}{d})=1) ,所以 (gcd(i,n)=d) 的个数为 (varphi(frac{n}{d})) 个。
    得到 (frac{1}{2} imes sum limits_{d|n} frac{n^2*varphi(frac{n}{d})}{d}+frac{n}{2})
    (frac{n^2*varphi(frac{n}{d})}{d}) 中提个 (n) 出来,再设 (d'=frac{n}{d})
    得到 (frac{1}{2}n imes (sum limits_{d'|n} d' imes varphi(d')+1))
    发现 (sum limits_{d'|n} d' imes varphi(d')) 为积性函数,直接筛即可。

    #include<iostream>
    #include<cstdio>
    #define int long long
    using namespace std;
    const int N=1e6+10;
    int T,n,f[N],p[N],cnt;
    bool flag[N];
    void O()
    {
    	flag[0]=1,flag[1]=1,f[1]=1;
    	for(int i=2;i<N;i++)
    	{
    		if(!flag[i]) p[++cnt]=i,f[i]=i*(i-1)+1;
    		for(int j=1;j<=cnt&&i*p[j]<N;j++)
    		{
    			flag[i*p[j]]=1;
    			if(i%p[j]==0)
    			{
    				f[i*p[j]]=f[i]+(f[i]-f[i/p[j]])*p[j]*p[j];
    				break;
    			}
    			f[i*p[j]]=f[i]*f[p[j]];
    		}
    	}
    }
    signed main()
    {
    	O();
    	scanf("%lld",&T);
    	while(T--)
    	{
    		scanf("%lld",&n);
    		printf("%lld
    ",n*(f[n]+1)/2);
    	}
    	return 0;
    }
    

    3
    (sum limits_{i=1}^n sum limits_{j=1}^m lcm(i,j))
    显然,原式即 (sum limits_{i=1}^n sum limits_{j=1}^m frac{i cdot j}{gcd(i,j)})
    枚举 (gcd) ,两数除以 (gcd) 后得到的数互质,得 (sum limits_{k=1}^n k cdot sum limits_{i=1}^{leftlfloor frac{n}{k} ight floor} sum limits_{j=1}^{leftlfloor frac{m}{k} ight floor} [gcd(i,j)=1] i cdot j)
    考虑把后面的式子拿出来计算,记 (sum(n,m)=sum limits_{i=1}^n sum limits_{j=1}^m [gcd(i,j)=1] i cdot j)
    接下来化简这个式子。利用莫比乌斯函数的性质,同时枚举 (gcd) ,得到 (sum limits_{k=1}^n sum limits_{i=1}^n sum limits_{j=1}^m [k|i][k|j]mu(d) cdot i cdot j)
    把后面的部分除以 (k) ,得到 (sum limits_{k=1}^n mu(k) cdot k^2 cdot sum limits_{i=1}^{leftlfloor frac{n}{k} ight floor} sum limits_{j=1}^{leftlfloor frac{m}{k} ight floor} i cdot j)
    发现式子的前半段可以前缀和预处理,后半段可以直接算,合并的部分用数论分块即可。
    (sum) 代回原式, (sum limits_{k=1}^n k cdot sum(leftlfloor frac{n}{k} ight floor,leftlfloor frac{m}{k} ight floor))
    发现这个式子依然可以用数论分块求解。

    #include<iostream>
    #include<cstdio>
    #define int long long
    using namespace std;
    const int N=1e7+10;
    const int P=20101009;
    int cnt,p[N],mu[N],f[N];
    int n,m;
    bool flag[N];
    int MIN(int x,int y)
    {
    	return x<y?x:y;
    }
    void O()
    {
    	mu[1]=1;
    	for(int i=2;i<N;i++)
    	{
    		if(!flag[i]) p[++cnt]=i,mu[i]=-1;
    		for(int j=1;j<=cnt&&i*p[j]<N;j++)
    		{
    			flag[i*p[j]]=1;
    			if(i%p[j]==0)
    			{
    				mu[i*p[j]]=0;
    				break;
    			}
    			mu[i*p[j]]=-mu[i];
    		}
    	}
    	for(int i=1;i<N;i++) f[i]=(f[i-1]+(mu[i]+P)%P*i%P*i%P)%P;
    }
    int sum(int n,int m)
    {
    	return (n*(n+1)/2%P)*(m*(m+1)/2%P)%P;
    }
    int calc(int n,int m)
    {
    	int l=0,r=0,ans=0;
    	while(l<=MIN(n,m))
    	{
    		l=r+1;
    		if(n/l==0||m/l==0) r=MIN(n,m);
    		else r=MIN(n/(n/l),m/(m/l));
    		ans=(ans+(f[r]-f[l-1]+P)%P*sum(n/l,m/l)%P)%P;
    	}
    	return ans;
    }
    int CALC(int n,int m)
    {
    	int l=0,r=0,ans=0;
    	while(l<=MIN(n,m))
    	{
    		l=r+1;
    		if(n/l==0||m/l==0) r=MIN(n,m);
    		else r=MIN(n/(n/l),m/(m/l));
    		ans=(ans+calc(n/l,m/l)*((l+r)*(r-l+1)/2%P)%P)%P;
    	}
    	return ans;
    }
    signed main()
    {
    	O();
    	scanf("%lld%lld",&n,&m);
    	printf("%lld",CALC(n,m));
    	return 0;
    }
    

    4
    (sum limits_{i=1}^n sum limits_{j=1}^m d(i cdot j)) ,其中 (d) 为约数个数函数。
    首先, (d) 函数有一个性质, (d(i cdot j)=sum limits_{x|i} sum limits_{y|j} [gcd(x,y)=1])
    推一推,
    (d(i cdot j)=sum limits_{x|i} sum limits_{y|j} [gcd(x,y)=1])
    ( =sum limits_{x|i} sum limits_{y|j} sum limits_{k|gcd(x,y)} mu(k))
    ( =sum limits_{k=1}^{min(i,j)} sum limits_{x|i} sum limits_{y|j} [k|gcd(x,y)] cdot mu(k))
    ( =sum limits_{k|i,k|j} mu(k) sum limits_{x|i} sum limits_{y|j} [k|gcd(x,y)])
    ( =sum limits_{k|i,k|j} mu(k) sum limits_{x|leftlfloor frac{i}{k} ight floor} sum limits_{y|leftlfloor frac{j}{k} ight floor} 1)
    ( =sum limits_{k|i,k|j} mu(k) d(leftlfloor frac{i}{k} ight floor) d(leftlfloor frac{j}{k} ight floor)) .
    把这个式子代回原式,
    ( sum limits_{i=1}^n sum limits_{j=1}^m d(i cdot j))
    (=sum limits_{i=1}^n sum limits_{j=1}^m sum limits_{k|i,k|j} mu(k) d(leftlfloor frac{i}{k} ight floor) d(leftlfloor frac{j}{k} ight floor))
    (=sum limits_{k=1}^{min(n,m)} sum limits_{i=1}^n sum limits_{j=1}^m [k|i][k|j]mu(k) d(leftlfloor frac{i}{k} ight floor) d(leftlfloor frac{j}{k} ight floor))
    (=sum limits_{k=1}^{min(n,m)} mu(k) sum limits_{i=1}^{leftlfloor frac{n}{k} ight floor} d(i) sum limits_{j=1}^{leftlfloor frac{m}{k} ight floor} d(j)) .
    发现每一部分均可前缀和预处理,用数论分块合并答案即可。

    #include<iostream>
    #include<cstdio>
    #define int long long
    using namespace std;
    const int N=5e4+10;
    int T,n,m,f[N],F[N];
    int cnt,p[N],d[N],t[N],mu[N];
    bool flag[N];
    int MIN(int x,int y)
    {
    	return x<y?x:y;
    }
    void O()
    {
    	flag[0]=1,flag[1]=1;
    	mu[1]=1,d[1]=1;
    	for(int i=2;i<N;i++)
    	{
    		if(!flag[i]) p[++cnt]=i,mu[i]=-1,d[i]=2,t[i]=1;
    		for(int j=1;j<=cnt&&i*p[j]<N;j++)
    		{
    			flag[i*p[j]]=1;
    			if(i%p[j]==0)
    			{
    				mu[i*p[j]]=0;
    				d[i*p[j]]=d[i]/(t[i]+1)*(t[i]+2);
    				t[i*p[j]]=t[i]+1;
    				break;
    			}
    			mu[i*p[j]]=-mu[i],d[i*p[j]]=d[i]*2,t[i*p[j]]=1;
    		}
    	}
    	for(int i=1;i<N;i++) f[i]=f[i-1]+mu[i];
    	for(int i=1;i<N;i++) F[i]=F[i-1]+d[i];
    }
    int calc(int n,int m)
    {
    	int l=0,r=0,ans=0;
    	while(l<=MIN(n,m))
    	{
    		l=r+1;
    		if(n/l==0||m/l==0) r=MIN(n,m);
    		else r=MIN(n/(n/l),m/(m/l));
    		ans+=(f[r]-f[l-1])*F[n/l]*F[m/l];
    	}
    	return ans;
    }
    signed main()
    {
    	O();
    	scanf("%lld",&T);
    	while(T--)
    	{
    		scanf("%lld%lld",&n,&m);
    		printf("%lld
    ",calc(n,m));
    	}
    	return 0;
    }
    
  • 相关阅读:
    Oracle数据库中truncate命令和delete命令的区别
    数组中只出现一次的数字
    数对之差的最大值
    SQL Server: Difference Between Locking, Blocking and Dead Locking
    字符串处理
    Phpcms_V9任意文件上传
    最初的梦想
    陪你走过漫长岁月
    基于MitM的RDP降级攻击
    CVE-2017-0358
  • 原文地址:https://www.cnblogs.com/zhs1/p/14208117.html
Copyright © 2020-2023  润新知