莫比乌斯反演(数论学习)

积性函数

定义

单位函数:\(\varepsilon(n)=[n=1]\),当且仅当n为1时为1,其余情况为0
恒等函数:\(id_k(n)=n^k,id_1(n)记作id(n)\)
常数函数:\(1(n)=1\),n为任意数函数值都为1
除数函数:\(\sigma_k(n)=\sum_{d|n}d^k\)\(\sigma_0(n)记作d(n),表示n的因数个数;\sigma_1(n)记作\sigma(n),可看作\sigma_0(0)*d\)
欧拉函数:\(\varphi(n)=\sum_{i=1}^n[gcd(i,n)=1]\),表示从1到n和n互质的数的个数
莫比乌斯函数

公式

\(\varepsilon=\mu * 1 \iff \varepsilon(n)=\sum_{d\mid n}\mu(d)\)

\(d=1 * 1 \iff d(n)=\sum_{d\mid n}1\)

\(\sigma=\operatorname{id} * 1 \iff\sigma(n)=\sum_{d\mid n}d\)

\(\varphi=\mu * \operatorname{id} \iff \varphi(n)=\sum_{d\mid n}d\cdot\mu(\frac{n}{d})\)

莫比乌斯函数

定义

\(\mu(n)= \begin{cases} 1&n=1\\0&n\text{ 含有平方因子}\\ (-1)^k&k\text{ 为 }n\text{ 的不同单个质因子的个数}\\\end{cases}\)

性质

  1. \(d|n表示d是n的因子\)

\(\sum_{d\mid n}\mu(d)= \begin{cases} 1&n=1\\ 0&n\neq 1\\ \end{cases}\)

\(=>\sum_{d\mid n}\mu(d)=[n=1]=\varepsilon(n)\)

\(=>\mu * 1 =\varepsilon\)

  1. 反演结论

\(\displaystyle [\gcd(i,j)=1]=\sum_{d\mid\gcd(i,j)}\mu(d)\)

实现

线性筛实现

void getMu() {
  mu[1] = 1;
  for (int i = 2; i <= n; ++i) {
    if (!flag[i]) prime[++tot] = i, mu[i] = -1;
    for (int j = 1; j <= tot && i * prime[j] <= n; ++j) {
      flag[i * prime[j]] = 1;
      if (i % prime[j] == 0) {
        mu[i * prime[j]] = 0;
        break;
      }
      mu[i * prime[j]] = -mu[i];
    }
  }
}

莫比乌斯反演

公式

\(设数论函数f(n),g(n)\)

\(f(n)=\sum_{d\mid n}g(d)=>g(n)=\sum_{d\mid n}\mu(d)f(\frac{n}{d})\)

\(f(n)=\sum_{n|d}g(d)=>g(n)=\sum_{n|d}\mu(\frac{d}{n})f(d)\)

\([gcd(i,j)=1]=\sum_{d|gcd(i,j)}\mu(d)\)

\(\sum_{d|n}\mu(d)=[n=1]\)

莫比乌斯函数和欧拉函数的关系
\(\frac{\phi(n)}{n}=\sum_{d|n}\frac{\mu(d)}{d}\)

例题

P2522 [HAOI2011]Problem b

P2522 [HAOI2011]Problem b

题意

\(对于给出的 n 个询问,每次求有多少个数对 (x,y),满足 a \le x \le b,c \le y \le d,且 \gcd(x,y) = k,\gcd(x,y) 函数为 x 和 y 的最大公约数。\)

题解

由题意可得求得公式为\(\sum_{i=a}^b\sum_{j=c}^d[\gcd(i,j)=k]\)

\(=>\sum_{i=1}^b\sum_{j=1}^d[\gcd(i,j)=k]-\sum_{i=1}^a\sum_{j=1}^d[\gcd(i,j)=k]-\sum_{i=1}^b\sum_{j=1}^c[\gcd(i,j)=k]+\sum_{i=1}^a\sum_{j=1}^b[\gcd(i,j)=k]\)

对于上述的任意部分可以进行推导

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

\(=>\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor}[\gcd(i,j)=1]\)

\(=>\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor}\sum_{d|gcd(i,j)}\mu(d)\)

\(=>\sum_{d=1}^{min(\lfloor \frac{n}{k} \rfloor,\lfloor \frac{m}{k}\rfloor)}\mu(d)\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\lfloor d|i \rfloor \sum_{j=1}^{\lfloor \frac{m}{k} \rfloor}\lfloor d|j \rfloor\)

\(=>\sum_{d=1}^{min(\lfloor \frac{n}{k} \rfloor,\lfloor \frac{m}{k}\rfloor)}\mu(d)\lfloor \frac{n}{kd} \rfloor \lfloor \frac{m}{kd} \rfloor\)

然后就能用数论分块求狮子 ^ _ ^ le

数论分块的基本公式就是
\(r=\lfloor \frac{n}{ \lfloor \frac{n}{l} \rfloor} \rfloor\)也就是r=n/(n/l)
然后用(r-l+1)*(n/l)求和

AC代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
const int mod=7;
const int maxn=5e4+10;
int n,k,t;
unordered_map<int,int>mp;
int a[maxn];

int mu[maxn],prime[maxn],vis[maxn],s=0,sum[maxn];

void mub(){//莫比乌斯函数
    //*表示和素数筛的区别之处
    mu[1]=1;//*
    for(int i=2;i<=maxn-10;i++){
        if(!vis[i]){
            prime[++s]=i;
            mu[i]=-1;//*
        }
        for(int j=1;j<=s&&prime[j]*i<=maxn-10;j++){
            vis[prime[j]*i]=1;
            if(i%prime[j]==0) {
                mu[i*prime[j]] = 0;//*
                break;
            }
            mu[i*prime[j]]=-mu[i];//*
        }
    }
    //预处理求和
    for(int i=1;i<=maxn-10;i++){
        sum[i]=sum[i-1]+mu[i];
    }
}

int slove(int n,int m){
    int res=0;
    for(int i=1,j;i<=min(n,m);i=j+1){
        j=min(n/(n/i),m/(m/i));
        res+=(sum[j]-sum[i-1])*(n/i)*(m/i);
    }
    return res;
}

signed main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>t;
    mub();
    while(t--){
        int a,b,c,d,k;
        cin>>a>>b>>c>>d>>k;
        cout<<slove(b/k,d/k)-slove(b/k,(c-1)/k)-slove((a-1)/k,d/k)+slove((a-1)/k,(c-1)/k)<<endl;
        //容差定理
    }
    return 0;
}

LCMSUM - LCM Sum

LCMSUM - LCM Sum

题意

给一个数n,求\(lcm(1,n)+lcm(2,n)+......+lcm(n,n)\)的和

题解

由题意可得求得公式为\(\sum_{i=1}^nlcm(i,n)\)

\(=>\sum_{i=1}^n\frac{i*n}{gcd(i,n)}\)

\(=>n*\sum_{i=1}^n\frac{i}{gcd(i,n)}\)

\(d=gcd(i,n)\)

\(当d|n时,gcd(\frac{i}{d},\frac{n}{d})=1\)

所以每个n的因子d的贡献等于所有数(\(\frac{i}{d}\))的和\(\frac{\phi(d)*d}{2}\)

\(n*\sum_{i=1}^n\frac{i}{gcd(i,n)}=>n*\sum_{d|n}\frac{\phi(d)*d}{2}\)

=_=用文件快读卡过去的

AC代码

#include<bits/stdc++.h>
using namespace std;
//#define int long long
#define ll long long
const int maxn=1e6+10;

int vis[maxn];
ll phi[maxn];
int s=0,prime[maxn];
void init() {
	phi[1] = 1;
	for(int i = 2; i <= maxn-10; i++) {
		if(!vis[i]) prime[++s] = i, phi[i] = i - 1;
		for(int j = 1; j <= s && prime[j] * i <= maxn-10; j++) {
			vis[prime[j] * i] = 1;
			if(i % prime[j] == 0) {
				phi[prime[j] * i] = prime[j] * phi[i];
				break;
			}
			phi[i * prime[j]] = phi[i] * (prime[j] - 1);
		}
	}
	for(int i = 2; i <= maxn-10; i++)
		phi[i] = phi[i] * i / 2;
}

void slove() {
	int n;
	rd(n); 
	long long res=0;
	for(int i=1; i*i<=n; i++) {
		if(n%i==0) {
			res+=phi[i];
			if(i*i<n) {
				res+=phi[n/i];
			}
		}
	}
	printf("%lld\n",n*res);
}

signed main() {
	init();
	int t;
	rd(t); 
	while(t--) {
		slove();
	}
	return 0;
}

「BZOJ 2154」Crash 的数字表格

「BZOJ 2154」Crash 的数字表格

题意

一张 n*m 的表格。每个格子里写了一个数字,其中第 i 行第 j 列的那个格子里写着数为 \(\text{lcm}(i, j)\),求这个表格中所有数的和mod20101009是多少

题解

由题意可得求得公式为\(\sum_{i=1}^n\sum_{j=1}^mlcm(m,n)\)

\(=>\sum_{i=1}^n\sum_{j=1}^m\frac{i*j}{gcd(i,j)}\)

\(=>\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j,gcd(\frac{i}{d},\frac{j}{d})=1}\frac{i*j}{d}\)

以d为基准转化成\(\sum_d^{min(n,m)}d\sum_{i=1}^{\lfloor\frac{i}{d} \rfloor}\sum_{j=1}^{\lfloor\frac{j}{d} \rfloor}[gcd(i,j)=1]i*j\)

可以将上面的公式转化成两个部分,\(\sum_d^{min(n,m)}d\)\(\sum_{i=1}^{\lfloor\frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor\frac{m}{d} \rfloor}[gcd(i,j)=1]i*j\)

对于后半部分设\(n2=\frac{n}{d},m2=\frac{m}{d}\),原式\(=>\sum_{d=1}^{min(n2,m2)}\sum_{d|i}^{n2}\sum_{d|j}^{m2}\mu(d)*i*j\)

然后提取出一个d使得原来的式子\(=>\sum_{d=1}^{min(n2,m2)}\mu(d)*d^2\sum_{i=1}^{\lfloor\frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor\frac{m}{d} \rfloor}i*j\)

然后分成两个部分\(\sum_{d=1}^{min(n2,m2)}\mu(d)*d^2\)\(\sum_{i=1}^{\lfloor\frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor\frac{m}{d} \rfloor}i*j\)

对于后半部分可以用公式求,前半部分可以预处理一下,求总和用分块处理

最后返回的结果再进行分块处理就ok了

mod半天发现自己mod错了但还以为是式子退错了就又推了半天的人就是逊啊

AC代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e7+10;
const int mod=20101009;

int mu[maxn],vis[maxn],prime[maxn],s=0;
void init(){
	mu[1]=1;
	for(int i=2;i<=maxn-10;i++){
		if(!vis[i]){
			mu[i]=-1;
			prime[++s]=i;
		}
		for(int j=1;j<=s&&prime[j]*i<=maxn-10;j++){
			vis[prime[j]*i]=1;
			if(i%prime[j]==0){
				mu[i*prime[j]]=0;
				break;
			}
			mu[i*prime[j]]=-mu[i];
		}
	}
	for(int i=1;i<=maxn-10;i++){
		mu[i]=(mu[i-1]%mod+(i*i%mod*(mu[i]+mod)%mod)%mod)%mod;
	}
}

int g(int n,int m){
	return ((((n+1)*n/2%mod)*((m+1)*m/2%mod)%mod)+mod)%mod;
}

int sum(int n,int m){
	int res=0;
	for(int i=1,j;i<=min(n,m);i=j+1){
		j=min(n/(n/i),m/(m/i));
		res=(res+((mu[j]-mu[i-1]+mod)%mod)*(g(n/i,m/i)%mod)%mod)%mod;
	}
	return res;
}

void slove() {
	int n,m;
	cin>>n>>m;
	int res=0;
	for(int i=1,j;i<=min(n,m);i=j+1){
		j=min(n/(n/i),m/(m/i));
		res+=(((i+j)*(j-i+1)/2%mod*sum(n/i,m/i)%mod)%mod+mod)%mod;
		res%=mod;
	}
	cout<<res%mod<<endl;
}

signed main() {
	init();
	slove();
	return 0;
}

posted @ 2021-07-18 16:37  碳素油墨  阅读(99)  评论(0)    收藏  举报