良心发现,痛改前非,时隔一年再回首 莫比乌斯反演

寒假疫情期间跟着lmm学了一遍,完全是懵逼到底状态,以至于后面考到或者做到相关知识的题目,完全是非洲人。今天跟着h老师重新学了一遍,虽然可能自己还是不会推🍔,但至少看得懂了吧

莫比乌斯反演


引入

\[F(n)=\sum_{d|n}f(d) \]

通过这个例子,可以暴力打出下列表

F f
F(1) f(1)
F(2) f(1)+f(2)
F(3) f(1)+f(3)
F(4) f(1)+f(2)+f(4)
F(5) f(1)+f(5)
F(6) f(1)+f(2)+f(3)+f(6)
F(7) f(1)+f(7)
F(8) f(1)+f(2)+f(4)+f(8)
... ...
转换一下,得到新表
f F
-- --
f(1) F(1)
f(2) F(2)-F(1)
f(3) F(3)-F(1)
f(4) F(4)-F(2)
f(5) F(5)-F(1)
f(6) F(6)-F(3)-F(2)+F(1)
f(7) F(7)-F(1)
f(8) F(8)-F(4)
... ...
看看能观察到\(f\)\(F\)之间存在什么规律??
🚨 重点关注\(f(1),f(4),f(6),f(8)\)
🧀 \(f(6)=F(\frac{6}{1})-F(\frac{6}{2})-F(\frac{6}{3})+F(\frac{6}{6})\)
发现规律 其实是知道公式了

\(F(\frac{n}{d})\)\(d\)质因数分解\(p_1^{k_1}...p_i^{k_i}\),如果各质数指数均为\(1\),则该\(F(\frac{n}{d})\)才会存在 ,特别地,当\(d=1\)时也一定存在

🍗:\(f(8)=F(\frac{8}{1})-F(\frac{8}{2})\)
\(F(\frac{8}{1}):1\)存在
\(F(\frac{8}{2}):2\)质因数分解\(2^1\),存在
\(F(2)=F(\frac{8}{4}):4\)质因数分解\(2^2\),不存在
\(F(1)=F(\frac{8}{8}):8\)质因数分解\(2^3\),不存在

\(F(\frac{n}{d})\)前面的符号取决于\(d\)所含质因子的个数/种类的奇偶性,\((-1)^k\)

🍗:\(f(6)=F(\frac{6}{1})-F(\frac{6}{2})-F(\frac{6}{3})+F(\frac{6}{6})\)
\(F(\frac{6}{1}):1\)不含任何质因子,系数为\((-1)^0=1\)
\(F(\frac{6}{2}):2\)含质因子\(2\),系数为\((-1)^1=-1\)
\(F(\frac{6}{3}):3\)含质因子\(3\),系数为\((-1)^1=-1\)
\(F(\frac{6}{6}):6\)含质因子\(2,3\),系数为\((-1)^2=1\)


公式

引入中发现的规律经过数学 提炼加工打磨 规范,将\((-1)^k\)定义为\(\mu (i)\)
便成为了一个优美的🍔

\[F(n)=\sum_{d|n}f(n)\Rightarrow f(n)=\sum_{d|n}\mu (d)F(\frac{n}{d}) \]

其中\(\mu(d)\)为莫比乌斯函数,定义如下:

  1. \(d=1,\mu(d)=1\)
  2. \(d\)能改写为\(p_1p_2...p_k\)互异质数的积,则\(\mu(d)=(-1)^k\)
  3. \(otherwise\),其余情况\(\mu(d)=0\)

性质

  • 性质一:
    对于任意的正整数\(n\)有:

\[\sum_{d|n}\mu(d)=\left\{ \begin{aligned} 1&&(n=1)\\ 0&&(n≠1) \end{aligned} \right. \]

证明:
Ⅰ. 当\(n=1\)时,\(\mu(1)=1\),显然成立
Ⅱ. 当\(n≠1\)时,将\(n\)质因数分解为\(p_1^{a_1}p_2^{a_2}...p_k^{a_i}\)
只有所有质因子的指数都为\(1\)的因数的\(\mu\)值不为\(0\)
则其中有\(x\)个不同质因子的个数为\(C_k^x\),于是有

\[\sum_{d|n}\mu(d)=C_k^0-C_k^1+C_k^2....=\sum_{i=0}^k(-1)^iC_k^i \]

🧀二项式展开公式为:

\[(X+Y)^n=\sum_{i=0}^nC_n^iX^iY^{n-i} \]

\(X=1,Y=-1\)带入即可得到:

\[\sum_{i=0}^k(-1)^iC_k^i=[1+(-1)]^k=0 \]


  • 性质二:
    对于任意的正整数\(n\)有:

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

证明:

\[\sum_{d|n}\frac{\mu(d)}{d}=\frac{\phi(n)}{n}\Leftrightarrow n\times \sum_{d|n}\frac{\mu(d)}{d}=\phi(n) \]

\(F(n)=n,f(n)=\phi(n)\),则有

\[f(n)=n\times \sum_{d|n}\frac{\mu(d)}{d}=\sum_{d|n}\mu(d)\frac{n}{d}=\sum_{d|n}\mu(d)F(\frac{n}{d}) \]

🚨不要忘记\(f(n)=\sum_{d|n}\mu(d)F(\frac{n}{d})\)成立的前提是\(F(n)=\sum_{d|n}f(d)\)
所以如果性质二要想成立,就必须再证明$$n=\sum_{d|n}\phi(d)$$
考虑从物理意义角度出发
\(k∈[1,n],gcd(n,k)=d\Rightarrow gcd(n/d,k)=1\),将\(k\)分到\(C_{n/d}\)类中
🍗:\(n=8\)

\[\left\{ \begin{aligned} 1,3,5,7&&gcd=1&&\phi(\frac{8}{1})=4\\ 2,6&&gcd=2&&\phi(\frac{8}{2})=2\\ 4&&gcd=4&&\phi(\frac{8}{4})=1\\ 8&&gcd=8&&\phi(\frac{8}{8})=1 \end{aligned} \right.\]


  • 性质三:
    莫比乌斯函数是一个积性函数

🧀
积性函数的定义:对于任意互质的整数\(a\)\(b\)有性质\(f(ab)=f(a)f(b)\)的数论函数
完全积性函数定义:对于任意的整数\(a\)\(b\)\(f(ab)=f(a)f(b)\)的数论函数
积性函数的性质:

  1. \(f(1)=1\)
  2. 积性函数的前缀和也是积性函数

模板

因为莫比乌斯函数是一个积性函数,我们就可以线性筛求出其值
由此联想到我们以前会的求质数的欧拉筛法

void sieve() {
	mu[1] = 1;
	for( int i = 2;i <= n;i ++ ) {
		if( ! vis[i] ) {
			vis[i] = 1;
			mu[i] = -1;
			prime[++ cnt] = i;
		}
		for( int j = 1;j <= cnt && i * prime[j] <= n;j ++ ) {
			vis[i * prime[j]] = 1;
			if( i % prime[j] == 0 ) {
				mu[i * prime[j]] = 0; //i*prime[j]这个数至少含有prime[j]^2 
				break;
			}
			mu[i * prime[j]] = - mu[i];//多了prime[j]这一种新的质因子 所以要与原来取相反数
			/*
			只要i*prime[j]含有pi^2
			早晚都会进if语句
			*/ 
		}
	}
}

公式证明

说到底,好像我们似乎貌似仿佛并没有证明这个定理,就直接提上裤子跑了


  • 形式一
    证明:

\[F(n)=\sum_{d|n}f(n)\Rightarrow f(n)=\sum_{d|n}\mu (d)F(\frac{n}{d})=\sum_{d|n}\mu(d)\sum_{k|\frac{n}{d}}f(k) \]

\({d|n,k|\frac{n}{d}\Rightarrow k|n}\),可以感性理解\(d\)取不同值,\(k\)会把\(n\)所有因数都枚举到
于是可以把\(k\)固定下来,更改\(d\)的取值范围,类似于两层循环顺序的互调??

\[=\sum_{k|n}f(k)\sum_{d|\frac{n}{k}}\mu(d) \]

将性质一的结论运用上,\(\sum_{d|\frac{n}{k}}\mu(d)\)当且仅当\(\frac{n}{k}=1\)时,求和莫比乌斯函数值\(≠0\)

\[=f(n) \]

证明过程用到了性质一,性质一本身其实是独立于公式的,所以并不是伪证


  • 形式二

\[F(n)=\sum_{n|d}f(d)\Rightarrow f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d) \]

证明:
与形式一的证明大致相同
\(k=\frac{d}{n}\)

\[=\sum_{k=1}^{+∞}\mu(k)F(n\times k)=\sum_{k=1}^{+∞}\mu(k)\sum_{n\times k|p}f(p)=\sum_{n|p}f(p)\sum_{k|\frac{p}{n}}\mu(k) \]

当且仅当\(\frac{p}{n}=1,p=n\)\(\sum_{k|\frac{n}{p}}\mu(k)=1\),其余全为\(0\)

\[=f(n) \]

一般这种形式更常用一些


莫比乌斯函数前缀和

基本上莫比乌斯反演的题目都会和分块绑定在一起,此时的莫比乌斯函数就需要进行前缀和
接下来就让我们一起来深挖一下分块部分
🍗

\[Q=\sum_{i|d}\mu(i)\lfloor{\frac{n}{i}}\rfloor \]

不妨设\(n=10\),如果按照\(\lfloor{\frac{n}{i}}\rfloor\)\(1\le i\le n\)进行分类
则有\(10=\{1\},5=\{2\},3=\{3\},2=\{4,5\},1=\{6,7,8,9,10\}\)
反映在平面直角坐标系上,看看是什么样👀
在这里插入图片描述
按照\(\sqrt{x}\)做分割线,前半段的\(x\)最多只有\(\sqrt{x}\)段,后半段的\(y\)最多只有\(\sqrt{x}\)
在这里插入图片描述拼接在一起,则\(\lfloor{\frac{n}{i}}\rfloor\)最多只有\(2\sqrt{n}\)个取值
\(n/i\)即为\(i\)所在的块,\(n/(n/i)\)则为该块右端点值

block = n / i;
r = n / block;

因为\([i,n/(n/i)]\)这一段的\(\lfloor{\frac{n}{i}}\rfloor\)均相等
所以我们就可以对\(\mu\)进行前缀和,直接\(O(1)\)计算出这一段区间的\(sum\)
就不必再老实地一个一个往后推
老实人永远会被出题人吊起来抽
这个思想会在后面的题里反复出现,所以提前放出来


题目练习

题解和代码后续会补上


完全平方数

  • solution

考虑二分答案,转化为求\([1,x]\)之间的无平方因子的个数
\(tot=0\)个质数乘积的平方的倍数的数的个数(\(1\)的倍数)
\(-1\)个质数乘积的平方的倍数的数的个数(\(4[2^2]\)的倍数,\(9[3^2]\)的倍数...)
\(+2\)个质数乘积的平方的倍数的数的个数(\(36[(2\times 3)^2]\)的倍数...)
发现每个乘积前面的系数刚好是\(\mu(i)\)
🍗:\(\mu(3)=-1\),故\(9\)对答案的贡献为负,\(\mu(6)=1\),故\(36\)对答案的贡献为正
\(x\)以内的\(i^2\)的个数为\(\frac{x}{i^2}\)

\[\Rightarrow tot(x)=\sum_{i=1}^{\sqrt{x}}\mu(i)\frac{x}{i^2} \]

这道题仅是莫比乌斯函数的运用,并非莫比乌斯反演

  • code
#include <cstdio>
#define int long long
#define maxn 1000005
int T, k, cnt;
int mu[maxn], prime[maxn];
bool vis[maxn];

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

int check( int x ) {
	int ans = 0;
	for( int i = 1;i * i <= x;i ++ )
		ans += x / ( i * i ) * mu[i];
	return ans;
}

signed main() {
	init();
	scanf( "%lld", &T );
	while( T -- ) {
		scanf( "%lld", &k );
		int l = 1, r = ( k << 1 );
		while( l <= r ) {
			int mid = ( l + r ) >> 1;
			if( check( mid ) >= k ) r = mid - 1;
			else l = mid + 1;
		}
		if( check( l ) == k ) printf( "%lld\n", l );
		else printf( "%lld\n", r );
	}
	return 0;
}

[HAOI2011]Problem b

  • solution

\(a\le x\le b,c\le y\le d\)二维差分成四个询问
每次询问\(1\le x\le n,1\le y\le m\)\(gcd(x,y)=k\)的数对数量
然后再转化为\(1\le x\le \frac{n}{k},1\le y\le \frac{m}{k}\)内的互质的数对数量

定义\(f(i):gcd(x,y)=i\)的数对数量,\(F(i):i|gcd(x,y)\)的数对数数量
\((1\le x\le n,1\le y\le m)\)

\[\Rightarrow F(i)=\lfloor{\frac{n}{i}}\rfloor\lfloor{\frac{m}{i}}\rfloor\Rightarrow f(i)=\sum_{i|d}\mu(\frac{d}{i})F(i)=\sum_{i|d}\mu(\frac{d}{i})\lfloor{\frac{n}{i}}\rfloor\lfloor{\frac{m}{i}}\rfloor \]

🧀\(\lfloor{\frac{n}{i}}\rfloor\lfloor{\frac{m}{i}}\rfloor\)其实是一段一段的区间,最多\(2(\sqrt{n}+\sqrt{m})\)个,从这里入手枚举
那么就需要对前面的\(\sum_{i|d}\mu(\frac{d}{i})\)前缀和

  • code
#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
#define maxn 50005
int n, a, b, c, d, k, cnt;
bool vis[maxn];
int prime[maxn], mu[maxn], pre[maxn];

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

int calc( int n, int m ) {
	if( n > m ) swap( n, m );
	int last, ans = 0;
	for( int i = 1;i <= n;i = last + 1 ) {
		last = min( n / ( n / i ), m / ( m / i ) );
		ans += ( pre[last] - pre[i - 1] ) * ( n / i ) * ( m / i );
	}
	return ans;
}

signed main() {
	init();
	scanf( "%lld", &n );
	for( int i = 1, a, b, c, d, k;i <= n;i ++ ) {
		scanf( "%lld %lld %lld %lld %lld", &a, &b, &c, &d, &k );
		a --, c --;
		a /= k, b /= k, c /= k, d /= k;
		printf( "%lld\n", calc( b, d ) - calc( a, d ) - calc( b, c ) + calc( a, c ) );
	}
	return 0;
}

YY的GCD

  • solution

既然要求\(gcd(x,y)\)为质数,而且发现这道题跟上一道problem b很像
先枚举质数,剩下的不就是求\(1\le x\le n,1\le y\le m,gcd(n,m)=1\)的数对数量??

定义\(f(i):gcd(x,y)=i\)的数对数量,\(F(i):i|gcd(x,y)\)的数对数数量
\((1\le x\le n,1\le y\le m)\)

\[F(i)=\lfloor{\frac{n}{i}}\rfloor\lfloor{\frac{m}{i}}\rfloor\Rightarrow f(i)=\sum_{i|d}\mu(\frac{d}{i})F(d)=\sum_{i|d}\mu(\frac{d}{i})\lfloor{\frac{n}{d}}\rfloor\lfloor{\frac{m}{d}}\rfloor$$🚨$gcd(x,y)=i$的质数已经提出来枚举了 $$\Rightarrow ans=\sum_{p}^{min(n,m)}f(1)=\sum_{p}^{min(n,m)}\sum_{d=1}^{min(n,m)}\mu(d)\lfloor{\frac{n}{d\times p}}\rfloor\lfloor{\frac{m}{d\times p}}\rfloor\]

如果止步于此,获得的只有美丽的黑色

\(k=d\times p\),则\(p=k/d\)

\[\Rightarrow ans=\sum_{k}^{min(n,m)}\lfloor{\frac{n}{k}}\rfloor\lfloor{\frac{m}{k}}\rfloor\sum_{p|k}\mu(\frac{k}{p}) \]

最后就只剩下对\(\sum_{p|k}\mu(\frac{k}{p})\)进行前缀和的处理
这里的处理略微不同于上一道题
可以通过暴力枚举质因子,对质因子的倍数进行\(\mu\)的累加,再前缀和处理出来

  • code
    遇得到哦🙄
    define int long long让我直接慢了1s多,导致\(T\)了,只能老老实实改long long才能顺利\(AC\)
#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 10000005
int T, cnt;
int sum[maxn], mu[maxn], prime[maxn];
long long pre[maxn];
bool vis[maxn];

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

int main() {
	init( 1e7 );
	scanf( "%d", &T );
	int n, m, r;
	long long ans;
	while( T -- ) {
		scanf( "%d %d", &n, &m );
		if( n > m ) swap( n, m );
		ans = 0;
		for( int i = 1;i <= n;i = r + 1 ) {
			r = min( n / ( n / i ), m / ( m / i ) );
			ans += 1ll * ( pre[r] - pre[i - 1] ) * ( n / i ) * ( m / i );
		}
		printf( "%lld\n", ans );
	}
	return 0;
}

[SDOI2014]数表

  • solution

一句话题意,令\(sum(i)\)表示\(i\)的因子和,给定\(n,m,a\),求🍔

\[\sum_{1\le i\le n,1\le j\le m}^{sum(gcd(i,j))\le a}sum(gcd(i,j)) \]

先思考此题的弱化版,即不考虑\(a\)

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

\(F(i)\)表示\(1\le x\le n, 1\le y\le m,i|gcd(x,y)\)的数对个数
\(f(i)\)表示\(1\le x\le n,1\le y\le m,gcd(x,y)=i\)的数对个数
则有

\[F(i)=\sum_{i|d}f(d),F(i)=\lfloor\frac{n}{i}\rfloor\lfloor\frac{m}{i}\rfloor \]

莫比乌斯反演

\[f(i)=\sum_{i|d}\mu(\frac{d}{i})F(d)=\sum_{i|d}\mu(\frac{d}{i})\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor \]

于是,有

\[ans=\sum_{i=1}^{min(n,m)}sum(i)f(i) \]

\[=\sum_{i=1}^{min(n,m)}sum(i)\sum_{i|d}\mu(\frac{d}{i})\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor \]

\[=\sum_{d=1}^{min(n,m)}\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor\sum_{i|d}sum(i)\mu(\frac{d}{i}) \]

如果现在知道\(\sum_{i|d}sum(i)\mu(\frac{d}{i})\),就可以\(O(sqrt{n})\)的计算出答案
\(sum(i)\)可以线性筛得到,与上一题类似,枚举\(i\)暴力更新倍数,前缀和即可,\(O(logn)\)

现在加上限制\(a\),又怎么做呢??
其实只有\(sum(i)\le a\)\(i\)才会产生贡献
自然而然的想到,将\(a,sum(i)\)分别排序,树状数组维护
本质就是树状数组在线插入
在这里插入图片描述

至于取模的问题,采取自然溢出的方式即可,具体细节可参见代码

  • code
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 100000
#define int long long
struct node {
	int n, m, a, id;
}q[maxn], s[maxn];
int prime[maxn], mu[maxn + 5], sum[maxn + 5], tree[maxn + 5], result[maxn + 5];
bool vis[maxn + 5];
int cnt;

bool cmp( node x, node y ) {
	return x.a < y.a;
}

void init() {
	mu[1] = 1;
	for( int i = 2;i <= maxn;i ++ ) {
		if( ! vis[i] ) prime[++ cnt] = i, mu[i] = -1;
		for( int j = 1;j <= cnt && i * prime[j] <= maxn;j ++ ) {
			vis[i * prime[j]] = 1;
			if( i % prime[j] == 0 ) {
				mu[i * prime[j]] = 0;
				break;
			}
			mu[i * prime[j]] = -mu[i];
		}
	}
	for( int i = 1;i <= maxn;i ++ )
		for( int j = i;j <= maxn;j += i ) sum[j] += i;
	for( int i = 1;i <= maxn;i ++ )
		s[i].a = sum[i], s[i].id = i;
	sort( s + 1, s + maxn + 1, cmp );
}

int lowbit( int x ) {
	return x & ( -x );
}

void add( int x, int v ) {
	for( int i = x;i < maxn;i += lowbit( i ) )
		tree[i] += v;
}

int query( int x ) {
	int ans = 0;
	for( int i = x;i;i -= lowbit( i ) )
		ans += tree[i];
	return ans;
}

int solve( int n, int m ) {
	if( n > m ) swap( n, m );
	int ans = 0, r;
	for( int i = 1;i <= n;i = r + 1 ) {
		r = min( n / ( n / i ), m / ( m / i ) );
		ans += ( n / i ) * ( m / i ) * ( query( r ) - query( i - 1 ) );
	}
	return ans;
}

void Add( int id ) {
	for( int i = 1;i * id <= maxn;i ++ )
		add( i * id, mu[i] * sum[id] );
}

signed main() {
	int Q;
	scanf( "%lld", &Q );
	for( int i = 1;i <= Q;i ++ )
		scanf( "%lld %lld %lld", &q[i].n, &q[i].m, &q[i].a ), q[i].id = i;
	sort( q + 1, q + Q + 1, cmp );
	init();
	int now = 0;
	for( int i = 1;i <= Q;i ++ ) {
		while( s[now + 1].a <= q[i].a && now < maxn ) Add( s[++ now].id );
		result[q[i].id] = solve( q[i].n, q[i].m );
	}
	int mod = 1ll << 31;
	for( int i = 1;i <= Q;i ++ )
		printf( "%lld\n", result[i] % mod );
	return 0;
}

[国家集训队]Crash的数字表格/JZPTAB

  • solution
    一句话题意,求🍔$$\sum_{i=1}n\sum_{j=1}mlcm(i,j)$$
  • \(step1:\) 枚举最大公因数

\[=\sum_{d=1}^{min(n,m)}\sum_{i=1}^n\sum_{j=1}^m\frac{i\times j}{d} \]

  • \(step2:\) 将最大公因数提到前面,循环上界压缩

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

  • \(step3:\) 利用\(\sum_{i|n}\mu(i)=[n=1]\),对原式不会造成改变

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

  • \(step4:\) 也将\(\mu\)提到前面,那么为了保证\(k|i,k|j\),枚举\(i,j\)变为直接枚举\(ki,kj\)

\[=\sum_{d=1}^{min(n,m)}d\sum_{k}^{min(\lfloor\frac{n}{d}\rfloor,\lfloor\frac{m}{d}\rfloor)}\sum_{ki}^{\lfloor\frac{n}{d}\rfloor}\sum_{kj}^{\lfloor\frac{m}{d}\rfloor}\mu(k)\times k^2\times i\times j \]

  • \(step5:\)\(k\)提出来

\[=\sum_{d=1}^{min(n,m)}d\sum_{k}^{min(\lfloor\frac{n}{d}\rfloor, \lfloor\frac{m}{d}\rfloor)}\mu(k)\times k^2\sum_{i=1}^{\lfloor\frac{n}{d\times k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d\times k}\rfloor}i\times j \]

在这里插入图片描述

⚡:
\(\sum_{k}^{min(\lfloor\frac{n}{d}\rfloor, \lfloor\frac{m}{d}\rfloor)}\mu(k)\times k^2\)使用老套路前缀和优化,之后\(O(1)\)查询
\(\sum_{i=1}^{\lfloor\frac{n}{d\times k}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d\times k}\rfloor}i\times j\)其实就是一个等差数列求积

\[\sum_{i=1}^n\sum_{j=1}^mi\times j=\frac{n\times (n+1)}{2}. \frac{m\times (m+1)}{2} \]

  • code
#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
#define mod 20101009
#define maxn 10000005
int mu[maxn], prime[maxn], sum[maxn];
bool vis[maxn];
int cnt, inv;

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

int seq( int n ) {
	return n % mod * ( n + 1 ) % mod * inv % mod;
}

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

int qkpow( int x, int y ) {
	int ans = 1;
	while( y ) {
		if( y & 1 ) ans = ans * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return ans;
}

signed main() {
	init();
	inv = qkpow( 2, mod - 2 );
	int n, m;
	scanf( "%lld %lld", &n, &m );
	int ans = 0;
	for( int d = 1;d <= min( n, m );d ++ )
		ans = ( ans + d * calc( n / d, m / d ) % mod ) % mod;
	printf( "%lld\n", ans );
	return 0;
} 

[SDOI2015]约数个数和

  • solution
    一句话题意,设\(d(x)\)表示\(x\)的约数和,求🍔

\[\sum_{i=1}^n\sum_{j=1}^md(i\times j) \]

首先有一个约数和的公式🍔 我不是很会证
在这里插入图片描述

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

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

转换一下枚举方式,考虑直接枚举因子

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

\(F(i)\)表示\(1\le x\le n,1\le y\le m,i|gcd(x,y)\)的约数和
\(f(i)\)表示\(1\le x\le n,1\le y\le m,i=gcd(x,y)\)的约数和,\(f(1)\)即为最终答案

\[F(p)=\sum_{p|d}f(d),F(p)=\sum_{i=1}^{\lfloor\frac{n}{p}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{p}\rfloor}\lfloor\frac{n}{i*p}\rfloor\lfloor\frac{m}{j*p}\rfloor \]

莫比乌斯反演

\[f(p)=\sum_{p|t}\mu(\frac{t}{p})F(t)=\sum_{p|t}\mu(\frac{t}{p})\sum_{i=1}^{\lfloor\frac{n}{t}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{t}\rfloor}\lfloor\frac{n}{i*t}\rfloor\lfloor\frac{m}{j*t}\rfloor \]

最后想求的答案就是\(f(1)\)

\[=\sum_{t=1}^{min(n,m)}\mu(t)F(t),F(t)=\sum_{i=1}^{\lfloor\frac{n}{t}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{t}\rfloor}\lfloor\frac{n}{it}\rfloor\lfloor\frac{m}{jt}\rfloor \]

在这里插入图片描述

对于求\(F(t)\)的方法,先计算出\(S(t)=\sum_{i=1}^t\lfloor\frac{t}{i}\rfloor\),则可以\(O(1)\)得到

  • code
#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
#define maxn 50005
int T, n, m, cnt;
int mu[maxn], prime[maxn], sum[maxn];
bool vis[maxn];

void init() {
	mu[1] = 1;
	for( int i = 2;i < maxn;i ++ ) {
		if( ! vis[i] ) prime[++ cnt] = i, mu[i] = -1;
		for( int j = 1;j <= cnt && i * prime[j] < maxn;j ++ ) {
			vis[i * prime[j]] = 1;
			if( i % prime[j] == 0 ) {
				mu[i * prime[j]] = 0;
				break;
			}
			mu[i * prime[j]] = -mu[i];
		}
	}
	for( int i = 1;i < maxn;i ++ ) mu[i] += mu[i - 1];
	for( int x = 1;x < maxn;x ++ ) {
		int ans = 0;
		for( int i = 1, j;i <= x;i = j + 1 ) {
			j = x / ( x / i );
			ans += ( j - i + 1 ) * ( x / i );
		}
		sum[x] = ans;
	}
}

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

signed main() {
	init();
	scanf( "%lld", &T );
	while( T -- ) {
		scanf( "%lld %lld", &n, &m );
		printf( "%lld\n", calc( n, m ) );
	}
	return 0;
}
posted @ 2021-01-12 20:43  读来过倒字名把才鱼咸  阅读(55)  评论(0编辑  收藏  举报