莫比乌斯反演

狄利克雷卷积

f, g 为数论函数

\(h=f*g\), 即 \(h(n)=\sum\limits_{d_1*d2=n}f(d_1)*g(d_2)\)

性质

  1. 满足交换律

  2. 满足结合律,即 \(p(n)=(f*g)*h=f*(g*h)=\sum\limits_{d_1*d_2*d_3=n}f(d_1)*g(d_2)*h(d_3)\)

  3. \(f,g\) 是积性函数,则 \(f*g\) 是积性函数(完全积性函数不满足此条性质)

常见卷积:

  1. \(1*\mu=e\)
  2. \(f*e=f\)
  3. \(id*\mu=\phi\)

莫比乌斯反演

\(f=g*1 \iff g=f*\mu\)

\(f(n)=\sum\limits_{d\mid n}g(d)\iff g(n)=\sum\limits_{d\mid n} f(d)*\mu(\frac nd)\)

证明:

引理1:\(1*\mu=e\)

引理2:\(f*e=f\)

  1. \(f=g*1\) , 两边同时卷上 \(\mu\)

    \(f*\mu=g*1*\mu=g*(1*\mu)=g*e=g\)

  2. \(g=f*\mu\), 两边同时卷上 \(1\)

    \(g*1=f*\mu*1=f*(\mu*1)=f*e=f\)

引理1,2 暴力带入狄利克雷卷积并利用积性函数的性质(两个积性函数卷起来仍是积性函数)即可证明

莫比乌斯反演 - 题目 - Daimayuan Online Judge

\(f=g*1\), 因此 \(g=f*\mu\), 线性筛求出 \(\mu\)

\(g(i)\) 的过程可考虑每个因子对 \(g(i)\) 的贡献,因此可以:

for (int d1 = 1; d1 <= n; d1++)
    	for (int d2 = 1; d1 *d2 <= n; d2++)
    		g[d1*d2] += f[d1] * mu[d2];

由调和级数,复杂度为 \(O(nlogn)\)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;
typedef long long ll;
typedef unsigned int uint;
const int N = 1e6 + 10;

uint n;
uint pr[N/5], p[N], cnt, mu[N], f[N], g[N];

unsigned int A,B,C;
inline unsigned int rng61()
{
    A ^= A << 16;
    A ^= A >> 5;
    A ^= A << 1;
    unsigned int t = A;
    A = B;
    B = C;
    C ^= t ^ A;
    return C;
}

void get_primes(uint n)
{
	p[1] = 1, mu[1] = 1;
	for (int i = 2; i <= n; i++)
	{
		if (!p[i])
		{
			p[i] = i;
			mu[i] = -1;
			pr[++cnt] = i;
		}
		for (int j = 1; j <= cnt && pr[j] <= n / i; j++)
		{
			p[i*pr[j]] = pr[j];
			if (p[i] == pr[j])
			{
				mu[i*pr[j]] = 0;
				break;
			}
			mu[i*pr[j]] = -mu[i];
		}
	}
}
int main()
{
    scanf("%d%u%u%u", &n, &A, &B, &C);
    for (int i = 1; i <= n; i++)
        f[i] = rng61();
    get_primes(n);
    for (int d1 = 1; d1 <= n; d1++)
    	for (int d2 = 1; d1 *d2 <= n; d2++)
    		g[d1*d2] += f[d1] * mu[d2];
	uint ans = 0;
    for (int i = 1; i <= n; i++) ans ^= g[i];
    printf("%u\n", ans);
    return 0;
}

互质数对 - 题目 - Daimayuan Online Judge

\(T\;(1<=T<=1000)\) 组询问,每次给出 \(n,m\;(1<=n,m<=10^7)\), 求 满足 \(1<=i<=n,\;1<=j<=m\)\(\gcd(i,j)=1\) 的对数

\[\begin{aligned} &\sum_{i=1}^n\sum_{j=1}^m\gcd(i,j)==1\\ &\sum_{i=1}^n\sum_{j=1}^me(\gcd(i,j))\\ &因为\;e=1*\mu\\ &所以\;\sum_{i=1}^n\sum_{j=1}^me(\gcd(i,j))=\sum_{i=1}^n\sum_{j=1}^m1*\mu(\gcd(i,j))\\ &=\sum_{i=1}^n\sum_{j=1}^m\sum_{d\mid \gcd(i,j)}\mu(d)\\ &=\sum_{d\mid \gcd(i,j)}\mu(d)\sum_{i=1}^n\sum_{j=1}^m1\\ &=\sum_{d\mid i,\;d\mid j}\mu(d)\sum_{i=1}^n\sum_{j=1}^m1\\ &=\sum_{d=1}^n\mu(d)\sum_{i=1且d\mid i}^n\sum_{j=1且d\mid j}^m1\\ &=\sum_{d=1}^n\mu(d)*\lfloor\frac ni\rfloor * \lfloor \frac mj \rfloor \\ \end{aligned} \]

因此求出 \(\mu\) 的前缀和,加上整除分块即可

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;
typedef long long ll;

const int N = 1e7 + 10;

int pr[N/5], p[N], cnt, mu[N], s[N];
void get_primes(int n)
{
	p[1] = mu[1] = s[1] = 1;
	for (int i = 2; i <= n; i++)
	{
		if (!p[i])
			pr[++cnt] = i, p[i] = i, mu[i] = -1;
		for (int j = 1; j <= cnt && pr[j] <= n / i; j++)
		{
			p[i*pr[j]] = pr[j];
			if (p[i] == pr[j])
			{
				mu[i*pr[j]] = 0;
				break;
			}
			mu[i*pr[j]] = -mu[i];
		}
	}
	for (int i = 2; i <= n; i++)
		s[i] = s[i-1] + mu[i];
}

int main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	get_primes(N - 10);
	while(T--)
	{
		int n, m;
		cin >> n >> m;
		if (n > m) swap(n, m);
		ll ans = 0;
		for (int l = 1; l <= n; l++)
		{
			int r = min(n / (n / l), m / (m / l));
			ans += (ll)(s[r] - s[l-1]) * (n / l) * (m / l);
			l = r;
		}
		cout << ans << endl;
	}
	return 0;
}

gcd之和 - 题目 - Daimayuan Online Judge

跟上一题类似

形如: \(\sum_{i=1}^n\sum_{j=1}^mf(\gcd(i,j))\) ,可用 \(g=f*\mu\) 找到 \(g\) ,再用 \(f=g*1\) 带入得

\(\sum_{i=1}^n\sum_{j=1}^m\sum\limits_{d\mid \gcd(i,j)}g(d)\) = \(\sum_{d=1}^ng(d)*\lfloor\frac ni\rfloor * \lfloor \frac mj \rfloor\)

本题中 \(g=id*\mu=\phi\)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;
typedef long long ll;

const int N = 1e7 + 10;

int pr[N/5], p[N], cnt, phi[N];
ll s[N];
void get_primes(int n)
{
	p[1] = phi[1] = s[1] = 1;
	for (int i = 2; i <= n; i++)
	{
		if (!p[i])
			pr[++cnt] = i, p[i] = i, phi[i] = i - 1;
		for (int j = 1; j <= cnt && pr[j] <= n / i; j++)
		{
			p[i*pr[j]] = pr[j];
			if (p[i] == pr[j])
			{
				phi[i*pr[j]] = phi[i] * pr[j];
				break;
			}
			phi[i*pr[j]] = phi[i] * (pr[j] - 1);
		}
	}
	for (int i = 2; i <= n; i++)
		s[i] = s[i-1] + phi[i];
}

int main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	get_primes(N - 10);
	while(T--)
	{
		int n, m;
		cin >> n >> m;
		if (n > m) swap(n, m);
		ll ans = 0;
		for (int l = 1; l <= n; l++)
		{
			int r = min(n / (n / l), m / (m / l));
			ans += (ll)(s[r] - s[l-1]) * (n / l) * (m / l);
			l = r;
		}
		cout << ans << endl;
	}
	return 0;
}

posted @ 2022-05-19 20:04  hzy0227  阅读(24)  评论(0编辑  收藏  举报