积性函数相关
以下内容中,\(a|b\)表示\(a\)是\(b\)的因数,\(a\not|b\)表示\(a\)不是\(b\)的因数
前置知识:特别基础的数论
积性函数
这部分设\(n\)质因数分解后为\(\prod\limits_{i=1}^kp_i^{c_i}\)
一些数论函数:
- 莫比乌斯函数:\(\mu(n)=\begin{cases}1\qquad n=1\\(-1)^k\qquad c_1=c_2=\cdots=c_k=1\\0\qquad\operatorname{otherwise}\end{cases}\)
- 欧拉函数(表示小于等于\(n\)且与\(n\)互质的数的个数)\(\varphi(n)=\begin{cases}1\qquad\qquad\qquad\qquad n=1\\n\prod\limits_{i=1}^k(1-\dfrac{1}{p_i})\qquad\operatorname{otherwise}\end{cases}\)
- 常函数:\(I(n)=1\)
- 单位函数:\(\epsilon(n)=[n=1]\)(若\(p\)成立则\([p]\)是1,否则\([p]\)为0)
- 恒等函数:\(id(n)=n\)
- 因数个数函数:\(d(n)=\sum\limits_{k|n}1\)
- 因数和函数:\(\sigma(n)=\sum\limits_{k|n}k\)
- 素因子个数函数:\(\Omega(n)=\sum\limits_{i=1}^kc_i\)
- 奇怪的函数\(\lambda(n)=(-1)^{\Omega(n)}\)
- 不同素因子个数函数:\(\omega(n)=k\)
积性函数:对于任意互质的两个数 \(a,b\),满足 \(f(ab)=f(a)f(b)\)的函数
如\(\mu,\varphi,d,\sigma\)
完全积性函数:对于任意的两个数 \(a,b\),满足 \(f(ab)=f(a)f(b)\)的函数
如\(I,\epsilon,id,\lambda\)
求欧拉函数和莫比乌斯函数
分解质因数,按照其定义和计算公式计算即可。
#include<bits/stdc++.h>
using namespace std;
int getphi(int n) {//phi
int tmp=n,ans=n;
for(int i=2; i*i<=tmp; ++i)
if(!(tmp%i)) {//满足这个的每一个i都是质数,因为之前所有质因子都被除掉了
ans-=ans/i;//即ans=ans*(i-1)/i;
while(!(tmp%i))tmp/=i;
}
return tmp>1?ans-ans/tmp:ans;//最后一个质因数要判断
}
int getmu(int n) {//mu
int tmp=n,ans=1;
for(int i=2; i*i<=tmp; ++i)
if(!(tmp%i)) {
ans=-ans;//乘上-1
tmp/=i;
if(!(tmp%i))//指数大于1
return 0;
}
return tmp>1?-ans:ans;//最后一个质因数的指数一定为1
}
int n;
int main() {
scanf("%d",&n);
printf("%d %d\n",getphi(n),getmu(n));
return 0;
}
欧拉函数和莫比乌斯函数的线性筛
bool isp[MAXN];
int prime[MAXN>>4],pcnt;
void init(int n) {
for(int i=2; i<=n; ++i) {
if(!isp[i])prime[++pcnt]=i;//0
for(int j=1,x; j<=pcnt&&(x=prime[j]*i)<=n; ++j) {
isp[prime[j]*i]=1;
if(!(i%prime[j]))break;//1
}
}
}
在上面的欧拉筛的代码中,\(0\)处判断\(i\)是否是质数,\(1\)处判断\(i,prime[j]\)是否互质了,那么我们可以通过这个部分进行线性筛欧拉函数和莫比乌斯函数。
根据两个函数的定义,当\(i\)为质数时,\(\varphi(i)=i-1,\mu(i)=-1\)
首先它们都是积性函数,所以没有break
时,直接相乘即可。
根据莫比乌斯函数的定义,当\(i,prime[j]\)不互质时\(\mu(i\times prime[j])=0\)
当\(i,prime[j]\)不互质时,我们设\(x=i\times prime[j]\)分解质因数后为\(p_1^{k_1}p_2^{k_2}\cdots p_c^{k_c}\)(不妨设其中\(p_1=prime[j]\),有\(k_1\geq2\))则
\(\varphi(x)=p_1^{k_1-1}(p_1-1)p_2^{k_2-1}(p_2-1)\cdots p_c^{k_c-1}(p_c-1)\)
\(\varphi(\dfrac{x}{p_1})=p_1^{k_1-2}(p_1-1)p_2^{k_2-1}(p_2-1)\cdots p_c^{k_c-1}(p_c-1)\)
可以发现\(\dfrac{\varphi(x)}{\varphi(\dfrac{x}{p_1})}=prime[j]\)
那就可以线性筛了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int MAXN=6000500;
bool isp[MAXN+1];
int phi[MAXN+1],mu[MAXN+1],pcnt,n,prime[MAXN+1];
int main() {
mu[1]=phi[1]=1;
for(int i=2; i<=MAXN; ++i) {
if(!isp[i]) {
prime[++pcnt]=i;
mu[i]=-1;
phi[i]=i-1;
}//素数时的值
for(int j=1,x; j<=pcnt&&(x=i*prime[j])<=MAXN; ++j) {
isp[x]=true;
if(i%prime[j]) {
mu[x]=-mu[i];
phi[x]=phi[i]*(prime[j]-1);
} else {
mu[x]=0;
phi[x]=phi[i]*prime[j];
break;
}//合数时的值
}
}
return 0;
}
来几道题:
先用一个\(d\)枚举\(\gcd(i,n)\)
\(\sum\limits_{d|n}\sum\limits_{i=1}^n[\gcd(i,n)=d]d\)
提出\(d\),将后面柿子的\(\gcd\)除以\(d\)
\(\sum\limits_{d|n}d\sum\limits_{i=1}^{\frac{n}{d}}[\gcd(i,\frac{n}{d})=1]\)
后面的部分其实就是\(\varphi(\dfrac{n}{d})\)
\(\sum\limits_{d|n}d\varphi(\dfrac{n}{d})\)
就可以计算了,时间复杂度\(O(\sqrt n)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,ans;
ll getphi(ll n) {
ll tmp=n,ans=n;
for(ll i=2; i*i<=tmp; ++i)
if(!(tmp%i)) {
ans-=ans/i;
while(!(tmp%i))tmp/=i;
}
return tmp>1?ans-ans/tmp:ans;
}
int main() {
scanf("%lld",&n);
for(ll i=1,k; i*i<=n; ++i)
if(!(n%i)) {
k=n/i;
ans+=getphi(k)*i;
if(i^k)ans+=getphi(i)*k;
}
printf("%llu\n",ans);
return 0;
}
我们可以假设C君的位置为\((0,0)\),建立一个平面直角坐标系。
那么队伍可以看做是一个\((0,0)\rightarrow(n-1,n-1)\)的\(n\times n\)的矩阵。
我们可以不考虑\((0,1)\)和\((1,0)\),最后在加上,就变成\((1,1)\rightarrow(n-1,n-1)\)的\((n-1)\times(n-1)\)的矩阵。
我们可以发现整个图形是对称的,所以只用算一半,乘上\(2\),再减去重复计算的\((1,1)\),我们就来算满足\(b\leq a\)的那一半。
我们可以发现,对于一个点\((a,b)\),当\(\gcd(a,b)>1\)时,会被\((\dfrac{a}{\gcd(a,b)},\dfrac{b}{\gcd(a,b)})\)挡住,例如\((4,2)\)会被\((2,1)\)挡住。(原理为相似三角形)
所以能看到的\((a,b)\)都有\(\gcd(a,b)=1\)。
设\(f_a\)表示满足\((a,b)\)能被看到的\(b\)有几个(\(1\leq b\leq a\leq n-1\))。
则这一半的个数为\(\sum\limits_{a=1}^n f_a\),整个的答案就是\(2(\sum\limits_{a=1}^n f_a)-1+2=2(\sum\limits_{a=1}^n f_a)+1\)。
由\(\gcd(a,b)=1,b\leq a\)得,\(f_a\)就是表示小于等于\(n\)且与\(n\)互质的数的个数,即\(\varphi(a)\),那么我们就把\(\varphi\)线性筛出来再求和即可。
注意要特判\(n=1\)的情况
#include <bits/stdc++.h>
using namespace std;
const int MAXN=40005;
bool isp[MAXN];
int prime[MAXN],pcnt,phi[MAXN],ans;
void init(int n) {
ans=phi[1]=1;
for(int i=2; i<=n; ++i) {//线性筛phi
if(!isp[i]) {
prime[++pcnt]=i;
phi[i]=i-1;
}
for(int j=1,x; j<=pcnt&&(x=prime[j]*i)<=n; ++j) {
isp[prime[j]*i]=1;
if(i%prime[j]) {
phi[x]=phi[i]*phi[prime[j]];
} else {
phi[x]=phi[i]*prime[j];
break;
}
}
ans+=phi[i];//顺便求和
}
}
int main() {
int n,q;
scanf("%d",&n);
init(--n);
printf("%d\n",n==0?0:ans*2+1);//特判0
return 0;
}
设\(f_k\)为\(1\leq x<y\leq n\)且\(\gcd(x,y)=k\)有多少组。
可以得到答案为\(\sum\limits_{k=1}^nkf_k\)
接下来考虑求\(f_k\)
首先\(f_k\)可以写成\(\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n[\gcd(i,j)=k]\)
对于\(\gcd(i,j)=k\)有\(\gcd(\dfrac{i}{k},\dfrac{j}{k})=1\),我们就可以转换成:
\(\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n[\gcd(\dfrac{i}{k},\dfrac{j}{k})=1]\)
把\(i,j\)都除以\(k\):
\(\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j=i+1}^{\lfloor\frac{n}{k}\rfloor}[\gcd(i,j)=1]\)
把\(j\)提到前面(对答案没有影响)
\(\sum\limits_{j=2}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{i=1}^{j-1}[\gcd(i,j)=1]\)
后面的\(\sum\limits_{i=1}^{j-1}[\gcd(i,j)=1]\)即是\(\varphi(j)\)(因为\(j>1\))
所以\(f_k=\sum\limits_{j=2}^{\lfloor\frac{n}{k}\rfloor}\varphi(j)=\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\varphi(i)-1\)
所以原式\(=\sum\limits_{k=1}^nk(\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\varphi(i)-1)=\sum\limits_{k=1}^nk(\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\varphi(i))-k\)
括号内线性筛+前缀和预处理,直接\(O(n)\)求即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e7+100;
int phi[MAXN],cnt,n,prime[MAXN];
ll sum[MAXN],ans;
int main() {
scanf("%d",&n);
sum[1]=phi[1]=1;
for(int i=2,p; i<=n; ++i) {//线性筛欧拉函数
if(!phi[i])prime[++cnt]=i,phi[i]=i-1;
for(int j=1; j<=cnt; ++j) {
p=prime[j]*i;
if(p>n)break;
if(!(i%prime[j])) {
phi[p]=phi[i]*prime[j];
break;
}
phi[p]=phi[i]*(prime[j]-1);
}
sum[i]=sum[i-1]+phi[i];//欧拉函数的前缀和
}
for(int i=1; i<=n; ++i)
ans+=sum[n/i]*i-i;
printf("%lld\n",ans);
return 0;
}
和上题差不多,可以算是双倍经验。
原式\(=\sum\limits_{i=1}^n\sum\limits_{j=1}^n\gcd(i,j)\)
\(=\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n\gcd(i,j)+\sum\limits_{i=1}^n\sum\limits_{j=1}^{i-1}\gcd(i,j)+\sum\limits_{i=1}^n\gcd(i,i)\)
可以发现前面两个都是上题要求的,后者就是\(\sum\limits_{i=1}^ni\)。
把上题的式子代进来:
\(\sum\limits_{i=1}^n\sum\limits_{j=1}^n\gcd(i,j)=2(\sum\limits_{k=1}^nk(\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\varphi(i))-k)+\sum\limits_{k=1}^nk=(\sum\limits_{k=1}^n2k(\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\varphi(i))-k)\)
这个是经典结论,可以记一下。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=1e7+100;
int phi[MAXN],cnt,n,prime[MAXN];
ll sum[MAXN],ans;
int main() {
scanf("%d",&n);
sum[1]=phi[1]=1;
for(int i=2,p; i<=n; ++i) {
if(!phi[i])prime[++cnt]=i,phi[i]=i-1;
for(int j=1; j<=cnt; ++j) {
p=prime[j]*i;
if(p>n)break;
if(!(i%prime[j])) {
phi[p]=phi[i]*prime[j];
break;
}
phi[p]=phi[i]*(prime[j]-1);
}
sum[i]=sum[i-1]+phi[i];
}
for(int i=1; i<=n; ++i)
ans+=((sum[n/i]<<1)-1)*i;
printf("%lld\n",ans);
return 0;
}