Oceanbase-01-安裝初體驗
狄利克雷卷積
f, g 為數論函式
\(h=f*g\), 即 \(h(n)=\sum\limits_{d_1*d2=n}f(d_1)*g(d_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)\)
-
若 \(f,g\) 是積性函式,則 \(f*g\) 是積性函式(完全積性函式不滿足此條性質)
常見卷積:
- \(1*\mu=e\)
- \(f*e=f\)
- \(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\)
-
若 \(f=g*1\) , 兩邊同時捲上 \(\mu\)
\(f*\mu=g*1*\mu=g*(1*\mu)=g*e=g\)
-
若 \(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)\)
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;
}