[笔记]莫比乌斯反演乱记

写在前面

本文章已同步发布在博主的博客园,也可以去那里看

这是蒟蒻第一次写这么长的博文

如果觉得写得凑合就点个支持吧 q w q qwq qwq

前置知识

积性函数狄利克雷卷积数论分块(这一篇去找gyh吧我讲也讲不好)(有空慢慢补)

Mobius函数

定义

莫比乌斯函数 μ ( n ) \mu(n) μ(n)定义为:

μ ( n ) = { 1 , n = 1 ( − 1 ) s , n = p 1 p 2 … p s ( s 为 n 的 本 质 不 同 的 质 因 子 个 数 ) 0 , n 有 平 方 因 子 \mu(n)=\left\{ \begin{aligned} 1, & n=1 \\ (-1)^{s}, & n=p_1p_2…p_s(s为n的本质不同的质因子个数)\\0,&n有平方因子\end{aligned} \right. μ(n)=1,(1)s,0,n=1n=p1p2pssnn

其中 p 1 p 2 … , p s p_1p_2…,p_s p1p2ps是不同素数。

可以看出,当 n n n没有平方因子时, μ ( n ) \mu(n) μ(n)非零。

μ \mu μ也是积性函数。

性质

莫比乌斯函数具有如下的性质:

∑ d ∣ n μ ( d ) = ϵ ( n ) = [ n = 1 ] \sum_{d|n}\mu(d)=\epsilon(n)=[n=1] dnμ(d)=ϵ(n)=[n=1]

使用狄利克雷卷积来表示,即

μ ∗ 1 = ϵ \mu*1=\epsilon μ1=ϵ

证明:

n = 1 n=1 n=1时显然成立。

n > 1 n>1 n>1,设 n n n s s s个不同的素因子,由于 μ ( d ) ≠ 0 \mu(d)\neq0 μ(d)=0当且仅当 d d d无平方因子,故 d d d中每个素因子的指数只能为 0 0 0 1 1 1

n n n的某个因子 d d d,有 μ ( d ) = ( − 1 ) i \mu(d)=(-1)^i μ(d)=(1)i,则它由 i i i个本质不同的质因子构成。因为质因子的总数为 s s s,所以满足上式的因子数有 C s i C_s^i Csi个。

所以我们就可以对于原式,转化为枚举 μ ( d ) \mu(d) μ(d)的值,同时运用二项式定理,故有

∑ d ∣ n μ ( d ) = ∑ i = 0 s C s i × ( − 1 ) i = ∑ i = 0 s C s i × ( − 1 ) i × 1 s − i = ( 1 + ( − 1 ) ) s \sum_{d|n}\mu(d)=\sum_{i=0}^{s}C_s^i\times(-1)^i=\sum_{i=0}^{s}C_s^i\times(-1)^i\times 1^{s-i}=(1+(-1))^s dnμ(d)=i=0sCsi×(1)i=i=0sCsi×(1)i×1si=(1+(1))s

该式在 s = 0 s=0 s=0 n = 1 n=1 n=1时为1,否则为 0 0 0

求莫比乌斯函数

因为莫比乌斯函数是积性函数,所以可以用线性筛

int n, cnt, p[A], mu[A];
bool vis[A];

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

莫比乌斯反演公式

f ( n ) f(n) f(n) g ( n ) g(n) g(n)为两个数论函数。

如果有

f ( n ) = ∑ d ∣ n g ( d ) f(n)=\sum\limits_{d|n}g(d) f(n)=dng(d)

则有

g ( n ) = ∑ d ∣ n μ ( d ) f ( n d ) g(n)=\sum\limits_{d|n}\mu(d)f(\frac{n}{d}) g(n)=dnμ(d)f(dn)

证明

  • 法一:对原式做数论变换

    1. ∑ d ∣ n g ( d ) \sum\limits_{d|n}g(d) dng(d)替换 f ( n ) f(n) f(n),即

      ∑ d ∣ n μ ( d ) f ( n d ) = ∑ d ∣ n μ ( d ) ∑ k ∣ n d g ( k ) \sum\limits_{d|n}\mu(d)f(\frac{n}{d})=\sum\limits_{d|n}\mu(d)\sum\limits_{k|\frac nd}g(k) dnμ(d)f(dn)=dnμ(d)kdng(k)

    2. 变换求和顺序得

      ∑ k ∣ n g ( k ) ∑ d ∣ n k μ ( n k ) \sum\limits_{k|n}g(k)\sum\limits_{d|\frac n k}\mu(\frac nk) kng(k)dknμ(kn)

    3. 因为 ∑ d ∣ n μ ( d ) = [ n = 1 ] \sum\limits_{d|n}\mu(d)=[n=1] dnμ(d)=[n=1],所以只有在 n k = 1 \frac{n}{k}=1 kn=1 n = k n=k n=k时才会成立,所以上式等价于

      ∑ d ∣ n [ n = k ] g ( k ) = g ( n ) \sum\limits_{d|n}[n=k]g(k)=g(n) dn[n=k]g(k)=g(n)

    得证

  • 法二:利用卷积

    原问题为:已知 f = g ∗ 1 f=g*1 f=g1,证明 g = f ∗ μ g=f*\mu g=fμ

    转化: f ∗ μ = g ∗ 1 ∗ μ = g ∗ ε = g f*\mu=g*1*\mu=g*\varepsilon=g fμ=g1μ=gε=g 1 ∗ μ = ε 1*\mu=\varepsilon 1μ=ε

    再次得证= =

小性质

[ gcd ⁡ ( i , j ) = 1 ] ⇔ ∑ d ∣ gcd ⁡ ( i , j ) μ ( d ) [\gcd(i,j)=1]\Leftrightarrow\sum\limits_{d|\gcd(i,j)}\mu(d) [gcd(i,j)=1]dgcd(i,j)μ(d)

证明

  • 法一:

    n = gcd ⁡ ( i , j ) n=\gcd(i,j) n=gcd(i,j),那么等式右边 = ∑ d ∣ n μ ( d ) = [ n = 1 ] = [ gcd ⁡ ( i , j ) = 1 ] = =\sum\limits_{d|n}\mu(d)=[n=1]=[\gcd(i,j)=1]= =dnμ(d)=[n=1]=[gcd(i,j)=1]=等式左边

  • 法二:

    利用单位函数暴力拆开: [ gcd ⁡ ( i , j ) = 1 ] = ε ( gcd ⁡ ( i , j ) ) = ∑ d ∣ gcd ⁡ ( i , j ) μ ( d ) [\gcd(i,j)=1]=\varepsilon(\gcd(i,j))=\sum\limits_{d|\gcd(i,j)}\mu(d) [gcd(i,j)=1]=ε(gcd(i,j))=dgcd(i,j)μ(d)

做题思路&&应用

利用狄利克雷卷积可以解决一系列求和问题。常见做法是使用一个狄利克雷卷积替换求和式中的一部分,然后调换求和顺序,最终降低时间复杂度。

经常利用的卷积有 μ ∗ 1 = ϵ \mu*1=\epsilon μ1=ϵ Id = φ ∗ 1 \text{Id}=\varphi*1 Id=φ1

还是以题为主吧,但是做的题也会单独写题解,毕竟要多水几篇博客的嘛/huaji

洛谷 P2522 [HAOI2011]Problem b

题目链接

我的题解

n n n组询问,每次给出 a , b , c , d , k a,b,c,d,k a,b,c,d,k,求 ∑ x = a b ∑ y = c d [ gcd ⁡ ( x , y ) = k ] \sum\limits_{x=a}^{b}\sum\limits_{y=c}^{d}[\gcd(x,y)=k] x=aby=cd[gcd(x,y)=k]

f ( n , m ) = ∑ i = 1 n ∑ j = 1 m [ gcd ⁡ ( i , j ) = k ] f(n,m)=\sum\limits_{i=1}^{n}\sum\limits_{j= 1}^{m}[\gcd(i,j)=k] f(n,m)=i=1nj=1m[gcd(i,j)=k]

那么根据容斥原理,题目中的式子就转化成了 f ( b , d ) − f ( b , c − 1 ) − f ( a − 1 , d ) + f ( a − 1 , c − 1 ) f(b,d)-f(b, c - 1) - f(a - 1,d) + f(a - 1, c - 1) f(b,d)f(b,c1)f(a1,d)+f(a1,c1)

所以我们接下来的问题就转化为了如何求 f f f的值

现在来化简 f f f的值

  1. 容易得出原式等价于 ∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ m k ⌋ [ gcd ⁡ ( i , j ) = 1 ] \sum\limits_{i = 1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j = 1}^{\lfloor\frac{m}{k}\rfloor}[\gcd(i,j) = 1] i=1knj=1km[gcd(i,j)=1]

  2. 因为 ϵ ( n ) = ∑ d ∣ n μ ( d ) = [ n = 1 ] \epsilon(n) =\sum\limits_{d|n}\mu(d)=[n=1] ϵ(n)=dnμ(d)=[n=1],由此可将原式化为
    ∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ m k ⌋ ∑ d ∣ g c d ( i , j ) μ ( d ) \sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{k}\rfloor}\sum\limits_{d|gcd(i,j)}\mu(d) i=1knj=1kmdgcd(i,j)μ(d)

  3. 改变枚举对象并改变枚举顺序,先枚举 d d d,得
    ∑ d = 1 min ⁡ ( n , m ) μ ( d ) ∑ i = 1 ⌊ n k ⌋ [ d ∣ i ] ∑ j = 1 ⌊ m k ⌋ [ d ∣ j ] \sum\limits_{d=1}^{\min(n,m)}\mu(d)\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}[d|i]\sum\limits_{j=1}^{\lfloor\frac{m}{k}\rfloor}[d|j] d=1min(n,m)μ(d)i=1kn[di]j=1km[dj]

    也就是说当 d ∣ i d|i di d ∣ j d|j dj时, d ∣ gcd ⁡ ( i , j ) d|\gcd(i,j) dgcd(i,j)

  4. 易得 1 ∼ ⌊ n k ⌋ 1\sim \lfloor\frac{n}{k}\rfloor 1kn中一共有 ⌊ n d k ⌋ \lfloor\frac{n}{dk}\rfloor dkn d d d的倍数,同理 1 ∼ ⌊ m k ⌋ 1\sim \lfloor\frac{m}{k}\rfloor 1km中一共有 ⌊ m d k ⌋ \lfloor\frac{m}{dk}\rfloor dkm d d d的倍数,于是原式化为 ∑ d = 1 min ⁡ ( n , m ) μ ( d ) ⌊ n d k ⌋ ⌊ m d k ⌋ \sum\limits_{d=1}^{\min(n,m)}\mu(d)\lfloor\frac{n}{dk}\rfloor\lfloor\frac{m}{dk}\rfloor d=1min(n,m)μ(d)dkndkm

此时已经可以 O ( n ) O(n) O(n)求解,但是过不了,因为有很多值相同的区间,所以可以用数论分块来做

先预处理 μ \mu μ,再用数论分块,复杂度 O ( n + T n ) O(n+T\sqrt n) O(n+Tn )

我的代码每次得分玄学,看评测机心情,建议自己写

/*
Author:loceaner
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int A = 1e6 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int n, a, b, c, d, k, cnt, p[A], mu[A], sum[A];
bool vis[A];

void getmu() {
	int MAX = 50010;
	mu[1] = 1;
	for (int i = 2; i <= MAX; i++) {
		if (!vis[i]) mu[i] = -1, p[++cnt] = i;
		for (int j = 1; j <= cnt && i * p[j] <= MAX; j++) {
			vis[i * p[j]] = true;
			if (i % p[j] == 0) break;
			mu[i * p[j]] -= mu[i];
		}
	}
	for (int i = 1; i <= MAX; i++) sum[i] = sum[i - 1] + mu[i];
}

int work(int x, int y) {
	int ans = 0ll;
	int max = min(x, y);
	for (int l = 1, r; l <= max; l = r + 1) {
		r = min(x / (x / l), y / (y / l));
		ans += (1ll * x / (l * k)) * (1ll * y / (l * k)) * 1ll * (sum[r] - sum[l - 1]); 
	}
	return ans;
}

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

signed main() {
	getmu();
	int T = read();
	while (T--) solve();
	return 0;
}

洛谷 P3455 [POI2007]ZAP-Queries

题目链接

我的题解

T T T组询问,每次询问求

∑ x = 1 a ∑ y = 1 b [ gcd ⁡ ( x , y ) = d ] \sum\limits_{x=1}^{a}\sum\limits_{y=1}^{b}[\gcd(x,y)=d] x=1ay=1b[gcd(x,y)=d]

因为我不喜欢用 x 、 y 、 a 、 b 、 d x、y、a、b、d xyabd,所以一一对应换成 i 、 j 、 n 、 m 、 k i、j、n、m、k ijnmk

直接淦式子(太长了CSDN加载不出来于是换成了截图)
在这里插入图片描述

现在就可以每次询问 O ( n ) O(n) O(n)做这道题了

但是跑不过啊,不过显然可以数论分块,所以我们就可以 O ( n ) O(\sqrt n) O(n )回答每次询问了

/*
Author:loceaner
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int A = 5e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int n, m, k, mu[A], p[A], sum[A], cnt;
bool vis[A];

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

int solve(int n, int m, int k) {
	int ans = 0, maxn = min(n, m);
	for (int l = 1, r; l <= maxn; l = r + 1) {
		r = min(n / (n / l), m / (m / l));
		ans += (sum[r] - sum[l - 1]) * (n / (k * l)) * (m / (k * l));
	}
	return ans;
}

int main() {
	getmu(50000);
	int T = read();
	while (T--) {
		n = read(), m = read(), k = read();
		cout << solve(n, m, k) << '\n';
	}
	return 0;
}

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

题目链接

我的题解

∑ i = 1 n ∑ j = 1 m lcm ( i , j ) (   m o d   20101009 ) \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\text{lcm}(i,j)(\bmod 20101009) i=1nj=1mlcm(i,j)(mod20101009)

容易想到原式等价于

∑ i = 1 n ∑ j = 1 m i ∗ j gcd ⁡ ( i , j ) \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\frac{i* j}{\gcd(i,j)} i=1nj=1mgcd(i,j)ij

枚举 i , j i,j i,j的最大公约数 d d d,显然 gcd ⁡ ( i d , j d ) = 1 \gcd(\frac id,\frac jd)=1 gcd(di,dj)=1,即 i d \frac id di j d \frac jd dj互质

∑ i = 1 n ∑ j = 1 m ∑ d ∣ i , d ∣ j , gcd ⁡ ( i d , j d ) = 1 i ∗ j d \sum\limits_{i=1}^{n}\sum\limits_{j=1}^m\sum\limits_{d|i,d|j,\gcd(\frac id,\frac jd)=1}\frac{i*j}d i=1nj=1mdi,dj,gcd(di,dj)=1dij

变换求和顺序

∑ d = 1 n d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ gcd ⁡ ( i , j ) = 1 ] i ∗ j \sum\limits_{d=1}^{n}d\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{d}\rfloor}[\gcd(i,j)=1]i*j d=1ndi=1dnj=1dm[gcd(i,j)=1]ij

s u m ( n , m ) = ∑ i = 1 n ∑ j = 1 m [ gcd ⁡ ( i , j ) = 1 ] i ∗ j sum(n,m)=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[\gcd(i,j)=1]i*j sum(n,m)=i=1nj=1m[gcd(i,j)=1]ij

对其进行化简,用 ε ( gcd ⁡ ( i , j ) ) \varepsilon(\gcd(i,j)) ε(gcd(i,j))替换 [ gcd ⁡ ( i , j ) = 1 ] [\gcd(i,j)=1] [gcd(i,j)=1]

∑ i = 1 n ∑ j = 1 m ∑ d ∣ gcd ⁡ ( i , j ) μ ( d ) ∗ i ∗ j \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\sum\limits_{d|\gcd(i,j)}\mu(d)*i*j i=1nj=1mdgcd(i,j)μ(d)ij

转化为首先枚举约数

∑ d = 1 min ⁡ ( n , m ) ∑ d ∣ i n ∑ d ∣ j m μ ( d ) ∗ i ∗ j \sum\limits_{d=1}^{\min(n,m)}\sum\limits_{d|i}^{n}\sum\limits_{d|j}^{m}\mu(d)*i*j d=1min(n,m)dindjmμ(d)ij

i = i ′ ∗ d , j = j ′ ∗ d i=i'*d,j=j'*d i=id,j=jd,则可以进一步转化

∑ d = 1 min ⁡ ( n , m ) μ ( d ) ∗ d 2 ∗ ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ i ∗ j \sum\limits_{d=1}^{\min(n,m)}\mu(d)*d^2*\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{d}\rfloor}i*j d=1min(n,m)μ(d)d2i=1dnj=1dmij

前半段可以处理前缀和,后半段可以 O ( 1 ) O(1) O(1)求,设

Q ( n , m ) = ∑ i = 1 n ∑ j = 1 m i ∗ j = n ∗ ( n + 1 ) 2 ∗ m ∗ ( m + 1 ) 2 Q(n,m)=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}i*j=\frac{n*(n+1)}{2}*\frac{m*(m+1)}{2} Q(n,m)=i=1nj=1mij=2n(n+1)2m(m+1)

显然可以 O ( 1 ) O(1) O(1)求解

到现在

s u m ( n , m ) = ∑ d = 1 min ⁡ ( n , m ) μ ( d ) ∗ d 2 ∗ Q ( ⌊ n d ⌋ , ⌊ m d ⌋ ) sum(n,m)=\sum\limits_{d=1}^{\min(n,m)}\mu(d)*d^2*Q(\lfloor\frac nd \rfloor,\lfloor\frac md\rfloor) sum(n,m)=d=1min(n,m)μ(d)d2Q(dn,dm)

可以用数论分块求解

回带到原式中

∑ d = 1 min ⁡ ( n , m ) d ∗ s u m ( ⌊ n d ⌋ , ⌊ m d ⌋ ) \sum\limits_{d=1}^{\min(n, m)}d*sum(\lfloor\frac nd \rfloor,\lfloor\frac md\rfloor) d=1min(n,m)dsum(dn,dm)

又可以数论分块求解了

然后就做完啦

/*
Author:loceaner
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;

const int A = 1e7 + 11;
const int B = 1e6 + 11;
const int mod = 20101009;
const int inf = 0x3f3f3f3f;

inline int read() {
    char c = getchar(); int x = 0, f = 1;
    for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

bool vis[A];
int n, m, mu[A], p[B], sum[A], cnt;

void getmu() {
    mu[1] = 1;
    int k = min(n, m);
    for (int i = 2; i <= k; i++) {
        if (!vis[i]) p[++cnt] = i, mu[i] = -1;
        for (int j = 1; j <= cnt && i * p[j] <= k; ++j) {
            vis[i * p[j]] = 1;
            if (i % p[j] == 0) break;
            mu[i * p[j]] = -mu[i];
        }
    }
    for (int i = 1; i <= k; i++) sum[i] = (sum[i - 1] + i * i % mod * mu[i]) % mod;
}

int Sum(int x, int y) { 
	return (x * (x + 1) / 2 % mod) * (y * (y + 1) / 2 % mod) % mod; 
}

int solve2(int x, int y) {
    int res = 0;
    for (int i = 1, j; i <= min(x, y); i = j + 1) {
        j = min(x / (x / i), y / (y / i));
        res = (res + 1LL * (sum[j] - sum[i - 1] + mod) * Sum(x / i, y / i) % mod) % mod;
    }
    return res;
}

int solve(int x, int y) {
    int res = 0;
    for (int i = 1, j; i <= min(x, y); i = j + 1) {
        j = min(x / (x / i), y / (y / i));
        res = (res + 1LL * (j - i + 1) * (i + j) / 2 % mod * solve2(x / i, y / i) % mod) % mod;
    }
    return res;
}

signed main() {
    n = read(), m = read();
    getmu();
    cout << solve(n, m) << '\n';
}

洛谷 P2257 YY的GCD

题目链接

给定 n , m n,m n,m,求二元组 ( x , y ) (x,y) (x,y)的个数,满足 1 ≤ x ≤ n , 1 ≤ y ≤ m 1\leq x\leq n,1\leq y\leq m 1xn,1ym,且 g c d ( x , y ) gcd(x,y) gcd(x,y)是素数。

n , m ≤ 1 0 7 n,m\leq 10^7 n,m107,自带多组数据,至多 1 0 4 10^{4} 104组数据。

思路与第一题Problem B类似,在这里不再赘述,只给出代码= =

#include <cmath> 
#include <cstdio>
#include <cstring> 
#include <iostream>
using namespace std;

const int A = 1e7 + 11; 

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1; 
	for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

bool vis[A];
long long sum[A];
int prim[A], mu[A], g[A], cnt, n, m;

void get_mu(int n) {
    mu[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) {
            mu[i] = -1;
            prim[++cnt] = i;
        }
        for (int j = 1; j <= cnt && prim[j] * i <= n; j++) {
            vis[i * prim[j]] = 1;
            if (i % prim[j] == 0) break;
            else mu[prim[j] * i] = - mu[i];
        }
    }
    for (int j = 1; j <= cnt; j++)
        for (int i = 1; i * prim[j] <= n; i++) g[i * prim[j]] += mu[i];
    for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + (long long)g[i];
}

signed main() {
    int t = read();
    get_mu(10000000);
    while (t--) {
        n = read(), m = read();
        if (n > m) swap(n, m);
        long long ans = 0;
        for (int l = 1, r; l <= n; l = r + 1) {
            r = min(n / (n / l), m / (m / l));
            ans += 1ll * (n / l) * (m / l) * (sum[r] - sum[l - 1]);
        }
        cout << ans << '\n';
    }
    return 0;
}

洛谷 P3327 [SDOI2015]约数个数和

题目链接

∑ i = 1 n ∑ j = 1 m d ( i j ) \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}d(ij) i=1nj=1md(ij)

d ( x ) d(x) d(x) x x x的约数个数和

需要用到

d ( i j ) = ∑ x ∣ i ∑ y ∣ j [ gcd ⁡ ( x , y ) = 1 ] d(ij)=\sum\limits_{x|i}\sum\limits_{y|j}[\gcd(x,y)=1] d(ij)=xiyj[gcd(x,y)=1]

证明我也不会

然后自己推导吧,在此不再赘述

/*
Author:loceaner
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int A = 5e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

bool vis[A];
int n, m, p[A], mu[A], cnt, sum[A];
long long g[A], ans;

void getmu(int n) {
	mu[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (!vis[i]) mu[i] = -1, p[++cnt] = i;
		for (int j = 1; j <= cnt && i * p[j] <= n; j++) {
			vis[i * p[j]] = 1;
			if (i % p[j] == 0) break;
			mu[i * p[j]] -= mu[i];
		}
	}
	for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + mu[i];
	for (int i = 1; i <= n; i++) {
		int ans = 0;
		for (int l = 1, r; l <= i; l = r + 1) {
			r = (i / (i / l));
			ans += 1ll * (r - l + 1) * (i / l);
		}
		g[i] = ans;
	}
}

signed main() {
	int T = read();
	getmu(50000);
	while (T--) {
		n = read(), m = read();
		int maxn = min(n, m);
		ans = 0;
		for (int l = 1, r; l <= maxn; l = r + 1) {
			r = min(n / (n / l), m / (m / l));
			ans += 1ll * (sum[r] - sum[l - 1]) * 1ll * g[n / l] * 1ll * g[m / l];
		}
		cout << ans << '\n';
	}
	return 0;
}

洛谷 P4449 于神之怒加强版

∑ i = 1 n ∑ j = 1 m gcd ⁡ ( i , j ) k (   m o d   1 e 9 + 7 ) \sum_{i=1}^{n}\sum_{j=1}^{m}\gcd(i,j)^k(\bmod 1e9+7) i=1nj=1mgcd(i,j)k(mod1e9+7)

还是直接淦式子

P = d x P=dx P=dx,则原式等于

∑ P = 1 min ⁡ ( n , m ) ⌊ n P ⌋ ⌊ m P ⌋ ∑ d ∣ P d k μ ( P d ) \sum_{P=1}^{\min(n,m)}\lfloor\frac n{P}\rfloor\lfloor\frac m{P}\rfloor\sum_{d|P}d^k\mu(\frac Pd) P=1min(n,m)PnPmdPdkμ(dP)

显然前面的 ⌊ n P ⌋ ⌊ m P ⌋ \lfloor\frac n{P}\rfloor\lfloor\frac m{P}\rfloor PnPm部分可以分块求解。

现在考虑后面的一部分,令

g ( n ) = ∑ d ∣ n d k μ ( n d ) g(n)=\sum_{d|n}d^k\mu(\frac nd) g(n)=dndkμ(dn)

容易得出这个函数是积性函数,所以我们就可以线性筛然后求出其前缀和

然后就做完了

/*
Author:loceaner
莫比乌斯反演
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;

const int A = 5e6 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar();
	int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

bool vis[A];
int T, n, m, k, f[A], g[A], p[A], cnt, sum[A];

inline int power(int a, int b) {
	int res = 1;
	while (b) {
		if (b & 1) res = res * a % mod;
		a = a * a % mod, b >>= 1;
	}
	return res;
}

inline int mo(int x) {
	if(x > mod) x -= mod;
	return x;
}

inline void work() {
	g[1] = 1;
	int maxn = 5e6 + 1;
	for (int i = 2; i <= maxn; i++) {
		if (!vis[i]) { p[++cnt] = i, f[cnt] = power(i, k), g[i] = mo(f[cnt] - 1 + mod); }
		for (int j = 1; j <= cnt && i * p[j] <= maxn; j++) {
			vis[i * p[j]] = 1;
			if (i % p[j] == 0) { g[i * p[j]] = g[i] * 1ll * f[j] % mod; break; }
			g[i * p[j]] = g[i] * 1ll * g[p[j]] % mod;
		}
	}
	for (int i = 2; i <= maxn; i++) g[i] = (g[i - 1] + g[i]) % mod;
}

inline int abss(int x) {
	while (x < 0) x += mod;
	return x;
}

signed main() {
	T = read(), k = read();
	work();
	while (T--) {
		n = read(), m = read();
		int maxn = min(n, m), ans = 0;
		for (int l = 1, r; l <= maxn; l = r + 1) {
			r = min(n / (n / l), m / (m / l));
			(ans += abss(g[r] - g[l - 1]) * 1ll * (n / l) % mod * (m / l) % mod) %= mod;
		}
		ans = (ans % mod + mod) % mod;
		cout << ans << '\n';
	}
	return 0;
}
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值