莫比乌斯反演学习笔记
莫比乌斯反演
-
数论分块
用处:用于快速计算一些含有除法向下取整的和式
for example: \(\sum_{i=1}^n \left\lfloor\frac{n}{i}\right\rfloor\)
就是将 \(\left\lfloor\frac{n}{i}\right\rfloor\) 相等的数同时计算
直接说结论吧:
对于一个常数 \(n\) 使得 \(\left\lfloor\frac{n}{i}\right\rfloor = \left\lfloor\frac{n}{j}\right\rfloor\) 并且 \(i\le j\le n\) 的 \(j\) 的最大值为\(\left\lfloor\frac{n}{\left\lfloor\frac{n}{i}\right\rfloor}\right\rfloor\)证明: 令 \(k=\left\lfloor\frac{n}{i}\right\rfloor\),\(\therefore k\le \frac{n}{i}\)
\(\therefore \left\lfloor\frac{n}{k}\right\rfloor \ge \left\lfloor\frac{n}{\frac{n}{i}}\right\rfloor = \left\lfloor i\right\rfloor = i\)
\(j=\max\)满足条件的所有\(i = i_{max} = \left\lfloor\frac{n}{i}\right\rfloor = \left\lfloor\frac{n}{\left\lfloor\frac{n}{i}\right\rfloor}\right\rfloor\)
\(By\quad Oi-Wiki\)
模板题:余数求和
题目大意:给定正整数\(n,k(n,k \le 1e9)\),求\(\sum_{i=1}^n{k\mod i}\)
solution:
考虑推式子,将其转为数论分块的形式
\(\sum_{i=1}^n k\mod i\)
\(=\sum_{i=1}^n k-\left\lfloor\frac{k}{i}\right\rfloor*i\)
\(=n*k-\sum_{i=1}^n\left\lfloor\frac{k}{i}\right\rfloor*i\)
后面考虑数论分块,假设左端点为\(l\),右端点为\(r\),则该段的值\(=(k/l)*\sum_{i=l}^ri\)
\(done.\)
\(code:\)
ll n,k;read(n,k); ll res = n*k; for(int l = 1,r;l <= n;l = r+1){ r = (k/l)?min(k/(k/l),n):n; res = res - k/l*(l+r)*(r-l+1)/2; } write(res);
-
狄利克雷卷积
对于两个数论函数\(f(x)\)和\(g(x)\),则它们的狄利克雷卷积得到的结果\(h(x)\)定义为\[h(x)=\sum_{d|x}f(d)g(\frac{n}{d})=\sum_{ab=x}f(a)g(b) \]简记为 \(h=f*g\)
简单性质:
1.\(交换律:f*g=g*f\)
2.\(结合律:(f*g)*h=f*(g*h)\)
3.\(分配律:(f+g)*h=f*h+g*h\)
两个结论:
1.两个积性函数的狄利克雷卷积也是积性函数
2.积性函数的逆元也是积性函数
详细证明请见\(oi-wiki\)
进入正题
-
莫比乌斯函数
\(\mu\)为莫比乌斯函数,定义为\(\mu=\begin{cases} 1,& n=1\\ 0,& n\text{含有平方因子}\\ (-1)^k,& k为n的本质不同质因子个数 \end{cases}\)
性质:
1.积性函数2.\(\sum_{d|x}\mu(d)=\begin{cases} 1,& n=1\\ 0,& n\ne1 \end{cases}\)
\(\quad\)
即\(\mu*1=\epsilon\)性质二证明:
设\(n=\prod_{i=1}^k p^{c_i}_i , n^{'}=\prod_{i=1}^k p_i\)
则有\(\sum_{d|n}\mu(d) = \sum_{d|n^{'}} = \sum_{i=0}^k\mathrm{C}_k^i*(-1)^i = (1+(-1))^k\)仅当\(k=0\)时为\(1\)反演结论:\([\gcd(i,j)=1]=\sum_{d|\gcd(i,j)}\mu(d)\)
-
线性筛筛莫比乌斯函数
运用定义式即可code:
vector<int> prime; int mu[N];bitset<N> pd; inline void mobius(int n){ mu[1]=1; for(int i=2;i<=n;++i){ if(!pd[i])mu[i]=-1,prime.emplace_back(i); for(int j:prime){ if(i*j>n) break; pd[i*j]=true; if(i%j == 0) break; else mu[i*j]=-mu[i]; } } }
-
莫比乌斯反演
- 应用反演结论\([\gcd(i,j)==1] = \sum_{d|\gcd(i,j)}\mu(d)\)
设\(f(n),g(n)\)为两个数论函数
-
形式一\(\qquad if\quad f(n)=\sum_{d|n}g(d),then\quad g(n)=\sum_{d|n}\mu(d)f(\frac{n}{d})\)
证明:
\(\begin{aligned} \sum_{d\mid n}\mu(d)f\left(\frac{n}{d}\right) &= \sum_{d\mid n}\mu(d)\sum_{k\mid \frac{n}{d}}g(k)\\ &= \sum_{k\mid n}g(k)\sum_{d\mid \frac{n}{k}}\mu(d)\\ &= \sum_{k\mid n}\left[\frac{n}{k} = 1\right]g(k)\\ &= g(n)\\ \end{aligned}\)
3.形式二\(\qquad if \quad f(n)=\sum_{n|d}g(d),then\quad g(n)=\sum_{n|d}f(d)\mu(\frac{d}{n})\)
证明:
\(\begin{aligned} \sum_{n|d}\mu(\frac{d}{n})f(d) &= \sum_{k=1}^{+\infty}\mu(k)f(kn)\\ &= \sum_{k=1}^{+\infty}\mu(k)\sum_{kn|d}^{+\infty}g(d)\\ &= \sum_{n|d}g(d)\sum_{k|\frac{d}{n}}\mu(k)\\ &= \sum_{n|d}g(d)\epsilon(\frac{d}{n})\\ &= g(n) \end{aligned}\)
-
应用
-
题目大意:对于给出的\(n\)个询问,每次求有多少个数对\((x,y)\),满足 \(a≤x≤b,c≤y≤d\),且 \(gcd(x,y)=k\)。
solution:
直接求\(\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-1}\sum_{j=1}^{d}[\gcd(i,j)=k]-\sum_{i=1}^b\sum_{j=1}^{c-1}[\gcd(i,j)=k]+\sum_{i=1}^{a-1}\sum_{j=1}^{c-1}[\gcd(i,j)=k]\)
则只需要推\(\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=k]\) (假设n\(\le\)m)
\(\begin{aligned} \sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=k] &= \sum_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}[\gcd(i,j)=1]\\ &= \sum_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}\sum_{d|\gcd(i,j)}\mu(d)\\ &=\sum_{d=1}^n\mu(d)\sum_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}[d|i][d|j]\\ &= \sum_{d=1}^n\mu(d)\left\lfloor\frac{n}{kd}\right\rfloor\left\lfloor\frac{m}{kd}\right\rfloor \end{aligned}\)
后面的整除分块加\(\mu\)前缀和即可
\(end\)
code:
const int N = 1e7+10; vector<int> prime; bitset<N> pd; int mu[N]; inline void mobius(int n){ mu[1] = 1; for(int i = 2;i <= n; ++i){ if(!pd[i]) prime.emplace_back(i),mu[i] = -1; for(auto j : prime){ if(i*j > n) break; pd[i*j] = true; if(i%j == 0) break; mu[i*j] = -mu[i]; } } for(int i = 1;i <= n; ++i) mu[i] += mu[i-1]; } inline int solve(int n,int m,int k){ if(n > m) swap(n,m); int res = 0; for(int l = 1,r = 0;l <= n;l = r+1){ r = min(n/(n/l),m/(m/l)); res = res+(mu[r]-mu[l-1])*(n/(l*k))*(m/(l*k)); } return res; } //main mobius(N-10); int T;read(T); while(T--){ int a,b,c,d,k;read(a,b,c,d,k); write(solve(b,d,k)-solve(a-1,d,k)-solve(b,c-1,k)+solve(a-1,c-1,k),'\n'); }
-
题目大意:\(给定 N,M,求 1≤x≤N,1≤y≤M 且 gcd(x,y) 为质数的 (x,y) 有多少对。\)
solution:
先写出暴力式子\(\sum_{p\in\mathbb{{p}}}\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=p]\)
推倒\(\begin{aligned} \sum_{p\in\mathbb{{p}}}\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=p] &= \sum_{p\in\mathbb{{p}}}\sum_{i=1}^{\left\lfloor\frac{n}{p}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{p}\right\rfloor}[\gcd(i,j)=1]\\ &= \sum_{p\in\mathbb{{p}}}\sum_{i=1}^{\left\lfloor\frac{n}{p}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{p}\right\rfloor}\sum_{d|\gcd(i,j)}\mu(d)\\ &= \sum_{p\in\mathbb{{p}}}\sum_{d=1}^n\mu(d)\left\lfloor\frac{n}{pd}\right\rfloor\left\lfloor\frac{m}{pd}\right\rfloor \end{aligned}\)
(一个套路:)用\(T\)替换\(pd\)
\(\begin{aligned} \sum_{p\in\mathbb{{p}}}\sum_{d=1}^n\mu(d)\left\lfloor\frac{n}{pd}\right\rfloor\left\lfloor\frac{m}{pd}\right\rfloor = \sum_{T=1}^n\left\lfloor\frac{n}{T}\right\rfloor\left\lfloor\frac{m}{T}\right\rfloor\sum_{d|T}\mu(d) \end{aligned}\)
后面的\(\sum_{d|T}\mu(d)\)可以暴力预处理求
\(end\)
code:
const int N = 1e7+10; vector<int> prime; bitset<N> pd; int mu[N]; ll f[N]; inline void mobius(int n){ mu[1] = 1; for(int i = 2;i <= n; ++i){ if(!pd[i]) prime.emplace_back(i),mu[i] = -1; for(auto j : prime){ if(i*j > n) break; pd[i*j] = true; if(i%j == 0) break; mu[i*j] = -mu[i]; } } for(auto i:prime){ for(int j = 1;j*i <= n; ++j){ f[i*j] += mu[j]; } } for(int i = 1;i <= n; ++i) f[i] += f[i-1]; } inline ll solve(int n,int m){ if(n > m) swap(n,m); ll res = 0; for(int l = 1,r = 0;l <= n;l = r+1){ r = min(n/(n/l),m/(m/l)); res = res+1ll*(f[r]-f[l-1])*(n/l)*(m/l); } return res; } //main mobius(N-10); int T;read(T); while(T--){ int n,m;read(n,m); write(solve(n,m),'\n'); }
进阶:
3.数表
\(有一张 n×m的数表,其第 i 行第 j 列(1≤i≤n,1≤j≤m)的数值为能同时整除 i 和 j 的所有自然数之和。给定 a,计算数表中不大于 a 的数之和。\)
多出来的\(\le a\)的限制很难评,那就先不考虑了
推式子
\(\begin{aligned} &\sum_{d=1}^n\sigma(d)\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=d]\\ &=\sum_{d=1}^n\sigma(d)\sum_{k=1}^n\mu(k)\left\lfloor\frac{n}{kd}\right\rfloor\left\lfloor\frac{m}{kd}\right\rfloor \end{aligned}\)
\(let\quad T = kd\)
\(\begin{aligned} &\sum_{T=1}^n\left\lfloor\frac{n}{T}\right\rfloor\left\lfloor\frac{m}{T}\right\rfloor\sum_{d|T}\sigma(d)\mu(\frac{T}{d}) \end{aligned}\)
后面明显的狄利克雷卷积,但因为那个\(\sigma(d)\le a\)的限制,所以就寄了。
那就暴力?no,可以考虑离线,将询问按\(a\)升序排序,每次将符合条件的\(\sigma(d)\)插入一个BIT中,查询前缀和即可。\(end\)
\(code:\)
const int N = 1e5+10; vector<int> prime; bitset<N> pd; int mu[N]; int s[N],low[N]; struct node{int val,id;}a[N]; struct Query{int n,m,a,id;}Q[N]; uint ans[N]; class BIT{ private: int tree[N]; inline int lowbit(int x){return (x&(-x));} public: int n = 0; inline void update(int pos,int val){for(int i = pos;i <= n;i += lowbit(i)) tree[i] += val;} inline int query(int pos){int res = 0;for(int i = pos; i;i -= lowbit(i)) res += tree[i];return res;} }T; inline void mobius(int n){ mu[1] = 1,s[1] = 1; for(int i = 2;i <= n; ++i){ if(!pd[i]) prime.emplace_back(i),mu[i] = -1,s[i] = i+1,low[i] = i; for(auto j : prime){ if(i*j > n) break; pd[i*j] = true; if(i%j == 0){ low[i*j] = low[i]*j; if(low[i] == i) s[i*j] = s[i]+i*j; else s[i*j] = s[i/low[i]]*s[low[i]*j]; break; } low[i*j] = j; s[i*j] = s[i]*s[j]; mu[i*j] = -mu[i]; } } for(int i = 1;i <= n; ++i) a[i] = {s[i],i}; sort(a+1,a+1+n,[](node x,node y){return x.val < y.val;}); } inline int solve(int n,int m){ int res = 0; for(int l = 1,r;l <= n;l = r+1){ r = min(n/(n/l),m/(m/l)); res = res + (T.query(r)-T.query(l-1))*(n/l)*(m/l); } return res; } //main int t;read(t); int mx = 0; for(int i = 1;i <= t; ++i){ int n,m,a;read(n,m,a); if(n > m) swap(n,m); T.n = max(T.n,n); mx = max(mx,n); Q[i]={n,m,a,i}; } sort(Q+1,Q+1+t,[](Query x,Query y){return x.a<y.a;}); mobius(mx); int now = 1; for(int i = 1;i <= t; ++i){ int n = Q[i].n,m = Q[i].m,lim = Q[i].a; while(now<=mx&&a[now].val<=lim){ for(int j = a[now].id;j <= mx;j += a[now].id) T.update(j,mu[j/a[now].id]*a[now].val); ++now; } ans[Q[i].id] = solve(n,m); } for(int i = 1;i <= t; ++i){ write(ans[i]&2147483647,'\n'); }
\(to\quad be\quad continued\)
-
本文来自博客园,作者:CuFeO4,转载请注明原文链接:https://www.cnblogs.com/hzoi-Cu/p/18193779