• 洛谷 P2257 YY的GCD 题解


    原题链接

    庆祝:

    数论紫题 (T4) 达成!

    莫比乌斯 (T1) 达成!

    yy 真是个 神犇

    前记

    之前我觉得: 推式子,直接欧拉筛,筛出个 (phi),然后乱推 (gcd) 就行了。

    现在我觉得: 推式子,还是欧拉筛,筛出个 (mu) ,然后乱推 (gcd) 就行了。

    前置知识:

    一定数学基础 ,欧拉筛。

    至少了解单位函数。(最好会整除分块哦)

    我们先引入 (mu) 的概念。

    [ mu_n = egin {cases} 1 , n=1 \ (-1)^k , n = prod_{i=1}^k p_i^{a_i},其中 a_i leq 1 space 且 space p_i 为素数 (即 space n space 不含平方因子)\ 0 ,n = prod_{i=1}^k p_i^{a_i},其中space p_i space 为素数,且存在 space a_i geq 2 (即 space n space 含平方因子)\ end{cases} ]

    你可能觉得,这有什么用?

    一开始我也这么觉得 但是呢,数学就是这样,先抛出一堆理论概念,然后抛个式子,接着题目就做出来了。

    首先,莫比乌斯函数是 积性函数 。比方说 (f) 为积性函数当且仅当:

    [f_{a imes b} = f_a imes f_b ]

    其中 (gcd(a,b)=1).

    顺便说一下,完全积性函数 就是:

    [f_{a imes b} = f_a imes f_b ]

    不需满足。 (mu) 是积性函数,所以,它可以用欧拉筛

    欧拉筛可以解决任何 满足积性函数性质 的函数。

    模板抛出来:

    inline void Euler(int n) {
    	mu[1]=1;
    	for(int i=2;i<=n;i++) {
    		if(!h[i]) mu[i]=-1,prime[++cnt]=i; //质数分解为1个质数的积,k=1,所以是 -1
    		for(int j=1;j<=cnt && prime[j]*i<=n;j++) {
    			h[i*prime[j]]=1;
    			if(i%prime[j]==0) break;
    			else mu[prime[j]*i]=-mu[i]; //奇偶变换
    		}
    	}
    }
    

    得出 (mu) 之后,你可能还是觉得:这东西的性质呢?似乎求了没什么用?

    没用还让你求啊

    [sum_{d|n} mu_d = [n=1] ]

    (n) 为正整数)

    证:

    (n=1) 时,显然 (sum_{d|n} mu_d = mu_1 = 1)

    (n ot = 1) 时,我们来 感性 的推式子。

    显然,所有含平方因子的数,我们不管它。因此质因数分解后,每个质数的幂次必然是 (1).

    显然可得:

    [sum_{d|n} mu_d ]

    [= C_k^0 - C_k^1 + C_k^2 + cdots + (-1)^k imes C_n^k ]

    [= sum_{i=0}^k (-1)^i imes C_k^i ]

    这个式子,显然是 (0).

    如果不知道为什么,建议重学组合

    如果你真的不知道怎么证,又学过了组合的一定理论,给你个提示:

    [C_k^i = C_k^{k-i} ]

    利用这个首尾配对即可。

    得证。

    得到这个性质后,我们再来重看一下它:

    [sum_{d|n} mu_d = [n=1] ]

    接着再抛一个定义:

    [sum_{d|n} frac{mu_d}{n} = frac{phi_n}{n} ]

    太懒不想证明了

    记住就行。如果真想证明的话,先去看看 莫比乌斯反演 里面的神奇性质吧。

    有了这两个性质,再加上一些套路即可。

    比方说呢,前 (3) 道我切的紫题,都和 (gcd) 有关,那么直接枚举 (gcd = d) ,然后随便交换一下枚举顺序,最终强行切到 (phi) 上,你就发现和 前缀和 有点关系,然后 (O(1)) 回答就行了.

    可是,你看这题的范围,像是 (O(1)) 回答吗?不然 (T leq 10^7) 好了,还开个 (4s),一看就是:

    [O(T sqrt{n}) ]

    先不说这个。推式子。(用 (prime) 表示素数或素数集合)

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

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

    [= sum_{d=1}^n sum_{i=1}^{lfloor frac{n}{d} floor} sum_{j=1}^{lfloor frac{n}{d} floor} [gcd(i,j) == 1] (d in prime) ]

    [= sum_{d=1}^n sum_{i=1}^{lfloor frac{n}{d} floor} sum_{j=1}^{lfloor frac{n}{d} floor} sum_{k | gcd(i,j)} mu_k (d in prime) ]

    ([gcd(i,j) == 1] = sum_{d | gcd(i,j)} mu_d),将公式中 (n) 变为 ([gcd(i,j) == 1])

    [= sum_{d=1}^n sum_{k=1}^{lfloor frac{n}{k} floor} mu_k imes lfloor frac{n}{k imes d} floor imes lfloor frac{m}{k imes d} floor (d in prime) ]

    (枚举 (k),改变 (i,j) 上限,把 (i) 变成 (lfloor frac{i}{d} floor imes d)(j) 同理)

    似乎已经最简,可是还不够。设 (T = k imes d).

    [= sum_{d=1}^n sum_{k=1}^{lfloor frac{n}{k} floor} mu_k imes lfloor frac{n}{T} floor imes lfloor frac{m}{T} floor (d in prime) ]

    [= sum_{T=1}^n lfloor frac{n}{T} floor imes lfloor frac{m}{T} floor sum_{d|T,d in prime} mu_{frac{T}{k}} ]

    对最后那个 (mu) 直接 欧拉筛 弄掉,前面的东西 整除分块

    另:

    如果整除分块你不知道,就先了解一个东西:

    [lfloor frac{n}{1} floor,lfloor frac{n}{2} floor cdots lfloor frac{n}{n} floor ]

    中,最多有 (sqrt{n}) 个不同的数值。

    如果不懂,可以先去我的博客学习一下。

    浅谈整除分块

    时间复杂度: (T sqrt{n}).

    实际得分: (100pts).

    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N=1e7+1;
    
    inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
    	int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
    
    ll sum[N]; bool h[N];
    int mu[N],g[N],prime[N];
    int cnt=0,T,n,m; ll ans=0;
    
    inline void Euler(int n) {
    	mu[1]=1;
    	for(int i=2;i<=n;i++) {
    		if(!h[i]) mu[i]=-1,prime[++cnt]=i;
    		for(int j=1;j<=cnt && prime[j]*i<=n;j++) {
    			h[i*prime[j]]=1;
    			if(i%prime[j]==0) break;
    			else mu[prime[j]*i]=-mu[i];
    		}
    	} //欧拉筛模板
    	for(int j=1;j<=cnt;j++)
    	for(int i=1;i*prime[j]<=n;i++)
    		g[i*prime[j]]+=mu[i]; //那个式子的一堆东西
    	for(int i=1;i<=n;i++) sum[i]=sum[i-1]+(ll)g[i];	 //前缀和优化
    } 
    
    int main(){
    	T=read(); Euler(N-1);
    	while(T--) {
    		ans=0;
    		n=read(),m=read();
    		if(n>m) swap(n,m);
    		for(int i=1;i<=n;) {
    			int t=min(n/(n/i),m/(m/i));
    			ans+=1ll*(n/t)*(m/t)*(sum[t]-sum[i-1]);
    			i=t+1; //整除分块模板
    		} printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    Django基于Form之登录和注册
    python中函数参数*args和**kw的区别
    django用户认证系统——自定义认证后台8
    django用户认证系统——重置密码7
    django用户认证系统——修改密码6
    django用户认证系统——注销和页面跳转5
    关于Shiro的盐值加密法的使用
    Shiro的登录验证【基于SpringMVC框架下】
    Shrio的登录验证过程中的密码验证,过程,及relam在applicationContext.xml的配置
    taglib自定义标签的使用
  • 原文地址:https://www.cnblogs.com/bifanwen/p/12523104.html
Copyright © 2020-2023  润新知