庆祝:
数论紫题 (T4) 达成!
莫比乌斯 (T1) 达成!
yy 真是个 神犇
前记
之前我觉得: 推式子,直接欧拉筛,筛出个 (phi),然后乱推 (gcd) 就行了。
现在我觉得: 推式子,还是欧拉筛,筛出个 (mu) ,然后乱推 (gcd) 就行了。
前置知识:
一定数学基础 ,欧拉筛。
至少了解单位函数。(最好会整除分块哦)
我们先引入 (mu) 的概念。
你可能觉得,这有什么用?
一开始我也这么觉得 但是呢,数学就是这样,先抛出一堆理论概念,然后抛个式子,接着题目就做出来了。
首先,莫比乌斯函数是 积性函数 。比方说 (f) 为积性函数当且仅当:
其中 (gcd(a,b)=1).
顺便说一下,完全积性函数 就是:
不需满足。 (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) 之后,你可能还是觉得:这东西的性质呢?似乎求了没什么用?
没用还让你求啊
((n) 为正整数)
证:
(n=1) 时,显然 (sum_{d|n} mu_d = mu_1 = 1)
(n ot = 1) 时,我们来 感性 的推式子。
显然,所有含平方因子的数,我们不管它。因此质因数分解后,每个质数的幂次必然是 (1).
显然可得:
这个式子,显然是 (0).
如果不知道为什么,建议重学组合
如果你真的不知道怎么证,又学过了组合的一定理论,给你个提示:
利用这个首尾配对即可。
得证。
得到这个性质后,我们再来重看一下它:
接着再抛一个定义:
太懒不想证明了
记住就行。如果真想证明的话,先去看看 莫比乌斯反演 里面的神奇性质吧。
有了这两个性质,再加上一些套路即可。
比方说呢,前 (3) 道我切的紫题,都和 (gcd) 有关,那么直接枚举 (gcd = d) ,然后随便交换一下枚举顺序,最终强行切到 (phi) 上,你就发现和 前缀和 有点关系,然后 (O(1)) 回答就行了.
可是,你看这题的范围,像是 (O(1)) 回答吗?不然 (T leq 10^7) 好了,还开个 (4s),一看就是:
先不说这个。推式子。(用 (prime) 表示素数或素数集合)
(([gcd(i,j) == 1] = sum_{d | gcd(i,j)} mu_d),将公式中 (n) 变为 ([gcd(i,j) == 1]))
(枚举 (k),改变 (i,j) 上限,把 (i) 变成 (lfloor frac{i}{d} floor imes d),(j) 同理)
似乎已经最简,可是还不够。设 (T = k imes d).
对最后那个 (mu) 直接 欧拉筛 弄掉,前面的东西 整除分块 。
另:
如果整除分块你不知道,就先了解一个东西:
中,最多有 (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;
}