关于莫比乌斯

莫比乌斯函数

也就是那个\(\mu(i)\) , 这个东西可以线性筛。
\(\mu(i] = \begin{cases} 1 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 1 \\ p_1 ^ {k_1}p_2^{k_2}...p_m^{k_m} \ \ (-1)^m \\ else \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 0 \end{cases}\)

两个性质

  1. \(\sum_{d | n} \mu(d) = [n = 1]\)
  2. \(\sum_{d | n} \frac{\mu(d)}{d} = \frac{\phi(n)}{n}\)
    第一个证明:
    根据定义,有超过一次的质因子都是0,那么只用考虑有没有某个质因子,也可以抽象成,有一堆的质因子,每一个上都可以填零或者填一,显然1的个数为奇数时ans=-1 , 否则ans=1。
    也就是看\(0 - ((1<<n)-1)\)中1的个数为奇数的-1的个数为偶数的显然ans=0(n != 1)
    第二个证明:
    这个就简单一点,全部通分,就是一个简单的容斥,

莫比乌斯反演

\(F[n] = \sum_{d | n} f(n)\)
\(f[n] = \sum_{d | n} \mu(d) F[n / d]\)
这个仍然可以用容斥来理解。
把所有n的约数取出,f(d)扔到一个数组里。要求的是最后一个f(n)
根据容斥,我们可以先加上所有的\(\mu(1) * F[n / 1] = F[n] = \sum_{d | n} f[d]\)
然后减去F[n / 2] , F[n / 3] (假设2,3是它的约数,意会一下)
然后再加上F[n / 6]

据说还有个式子
\(F[n] = \sum_{n | d} f[d]\)
\(f[n] = \sum_{n | d} F[d] * \mu(d / n)\)

P2522 [HAOI2011]Problem b

对于给出的 n 个询问,每次求有多少个数对 (x,y),满足 a <= x <= b , c <= y <= d ,且 gcd(x,y) = k; 所有出现的数 <= 5e4

这个是个经典的题
用之前的反演练习一下。
设f(x) 为gcd(i , j) = x 的对数
设F(x) 为 x | gcd(i , j) 的对数
那么显然 \(F[x] = \sum_{x | d} f[d]\)
反演可得 \(f[x] = \sum_{x | d} \mu(d / x) F[d]\)
再来考虑F[x]是啥。\(F[x] = \frac{n}{x} * \frac{m}{x}\)(下取整)
那么 \(f[k] = \sum_{k | d} \mu(d / k) * \frac{n}{d} * \frac{m}{d}\)
枚举i=d/k然后预处理\(\sum \mu\) 时间复杂度 \(O(q \sqrt n)\)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<bitset>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 5e4+10;
inline int read()
{
    register int x = 0 , f = 0; register char c = getchar();
    while(c < '0' || c > '9') f |= c == '-' , c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
    return f ? -x : x;
}
int tot;
int vis[N] , prime[N] , mu[N];

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

int calc(int n , int m , int k)
{
	n /= k; m /= k; int ans = 0; if(n > m) swap(n , m);
	for(int l = 1 , r ; l <= n ; l = r + 1)
	{
		r = min(n / (n / l) , m / (m / l));
		ans += (mu[r] - mu[l-1]) * (n / l) * (m / l);
	}
	return ans;
}

void solve()
{
	int a = read() , b = read() , c = read() , d = read() , k = read();
	cout << calc(b , d , k) - calc(a - 1 , d , k) - calc(b , c - 1 , k) + calc(a - 1 , c - 1 , k) << '\n';
	return ;
}

int main()
{
	int T = read(); 
	Init(5e4);
	while(T--) solve();
//	cout << calc(4 , 4 , 2) << '\n';
	return 0;
}

P2257 YY的GCD

给定 N, MN,M,求 1 <= x && x <= n且 1 <= y && y <= m 且 gcd(x , y) 为质数的 (x,y) 有多少对。
n , m <= 1e7

和上一题类似,对着一个式子推啊推啊推 设n <= m

\[ans = \sum_{p \ belong \ prime \ \ \ p | n } f(p); \]

展开

\[ans = \sum_{p \ belong \ prime \ \ \ p | n } \sum_{p | d} \mu(d / p) \frac{n}{d} \frac{m}{d} \]

考虑先枚举d

\[ans = \sum_{d = 1} ^ n \frac{n}{d} \frac{m}{d} \sum_{p \ belong \ prime \ \ \ p | d } \mu(d / p) \]

显然这东西可以预处理
\(\sum_{p \ belong \ prime \ \ \ p | d } \mu(d / p)\)
然后对其求个前缀和就和上一题一样了

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<bitset>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 1e7+10;
inline int read()
{
    register int x = 0 , f = 0; register char c = getchar();
    while(c < '0' || c > '9') f |= c == '-' , c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
    return f ? -x : x;
}
int tot;
int vis[N] , prime[N] , mu[N];
LL f[N] , sum[N];

void Init(int maxn)
{
	mu[1] = 1;
	for(int i = 2 ; i <= maxn ; ++i)
	{
		if(!vis[i]) prime[++tot] = i , mu[i] = -1;
		for(int j = 1 ; j <= tot && i * prime[j] <= maxn ; ++j)
		{
			vis[i * prime[j]] = 1;
			if(i % prime[j] == 0) break;
			mu[i * prime[j]] = -mu[i];
		}
	}
	for(int i = 1 ; i <= tot ; ++i)
		for(int j = 1 ; j * prime[i] <= maxn ; ++j)
			f[j * prime[i]] += mu[j];
	for(int i = 1 ; i <= maxn ; ++i) sum[i] = sum[i-1] + f[i];
	return ;
}

LL calc(int n , int m)
{
	LL ans = 0; if(n > m) swap(n , m);
	for(int l = 1 , r ; l <= n ; l = r + 1)
	{
		r = min(n / (n / l) , m / (m / l));
		ans += (sum[r] - sum[l-1]) * (n / l) * (m / l);
	}
	return ans;
}

void solve()
{
	int n = read() , m = read();
	cout << calc(n , m) << '\n';
	return ;
}

int main()
{
	Init(1e7); int T = read(); 
	while(T--) solve();
	return 0;
}
posted @ 2020-04-17 09:41  沙野博士  阅读(283)  评论(0)    收藏  举报