简单数论杂题

· · 算法·理论

::::info[AI使用声明] 使用 deepseek 润色,保证人类贡献远大于 AI 贡献。 ::::

Coprime

对于一个长度为 n 的数组 a_1,a_2,\dots,a_n,我们定义:

  • 两两互质,对于任意的 1\le i<j\le n 的一对 (i,j) 满足 a_i,a_j 互质(\gcd(a_i,a_j)=1)。
  • 集合互质。如果 a 不满足两两互质且 \gcd\{a_1,a_2,\dots,a_n\}=1 那么称 a 数组集合互质。

如果 a 两两互质输出 pairwise coprime,如果是集合互质输出 setwise coprime,如果均不是输出 not coprime

我们设一个桶 \text{cnt},\text{cnt}_p 表示有多少 a_i 有质因子 p。

那么我们如果要两两互质,任意的 a_i,a_j 不应该存在公共的质因数,也就是对于所有 \text{cnt}_p 都应该满足 \text{cnt}_p \le 1(最多只能有一个 a_i 有因数 p)。

然后考虑集合互质。设 \gcd\{a_1,a_2,\dots,a_n\}=x。若 x>1,取 x 的任意一个质因子 p,则 p 整除所有 a_i,即 \text{cnt}_p=n。反过来,若对某个质数 p 有 \text{cnt}_p=n,则 p 整除所有 a_i,故 p\mid x。因此 x>1 当且仅当存在质数 p 使 \text{cnt}_p=n;等价地,x=1 当且仅当对所有质数 p 都有 \text{cnt}_p\ne n。这样我们就解决了集合互质。

::::success[abc177_e]

#include<bits/stdc++.h>
using namespace std;

const int N = 1e6 + 5;
const int V = 1e6;

int n,a[N],cnt[N];
void Get(int x){
    for(int i = 2;i * i <= x;i ++){
        if(!(x % i)) {
            while(!(x % i)) x /= i;
            cnt[i] ++;
        }
    }
    cnt[x] ++;
} 
int main(){
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++) {
        scanf("%d",&a[i]);
        Get(a[i]);
    }
    int b1 = 0,b2 = 0;
    for(int i = 2;i <= V;i ++){
        if(cnt[i] > 1) b1 = 1;
        if(cnt[i] == n) b2 = 1;
    }
    if(!b1) puts("pairwise coprime");
    else if(!b2) puts("setwise coprime");
    else puts("not coprime");
    return 0;
}

::::

Orac and LCM

给定一个长度为 n 的序列 a。构造多重集 t=\{\text{lcm}\{a_i,a_j\}\mid i<j\} 求出 \gcd(t),即计算给定序列中所有元素对的最小公倍数的最大公约数。

设 v_p(n) 表示素数 p 在 n 的质因数分解中的指数,那么得到

\text{lcm}(a,b)=\prod_{p \text{ is a prime}}p^{\max(v_p(a),v_p(b))}

固定一个质数 p,设所有 a_i 中 p 的指数按从小到大排列为 e_1\le e_2\le\dots\le e_n。

对于任意一对 (i,j),\text{lcm}(a_i,a_j) 中 p 的指数为 \max(e_i,e_j)。而最终答案的 \gcd 会对所有这些指数取最小值。

在所有 i<j 的配对中,\max(e_i,e_j) 的最小值恰好是 e_2(取 e_1 与 e_2 对应的两个下标即可得到 e_2;而任何一对 i\ne j 都有 \max(e_i,e_j)\ge \max(e_1,e_2)=e_2)。因此最小的指数 e_1 永远不会成为某个 \max 的结果(除非所有 e_i 都相等,此时 e_1=e_2,结论仍然成立),所以最终答案中 p 的指数就是第二小的指数 e_2。

注:这些所谓的最小和第二小都不是严格最小,允许第二小和最小完全相同。

::::success[CF1349A]

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N = 2e6 +5; 
int n,a[N],cnt[N],tmp[N],ans[N];
void Get(int x){
    for(int i = 2;i * i <= x;i ++){
        int sum = 0; 
        if(!(x % i)){
            while(!(x % i)) x /= i,sum ++;
            if(sum > cnt[i]){
                ans[i] = min(ans[i],sum);
            } else {
                ans[i] = cnt[i];
                cnt[i] = sum;
            }
            tmp[i] ++;
        }
    }
    tmp[x] ++;
    int sum = 1;
    if(sum > cnt[x]){
        ans[x] = min(ans[x],sum);
    } else {
        ans[x] = cnt[x];
        cnt[x] = sum;
    }
} 
signed main(){
    scanf("%lld",&n);
    for(int i = 1;i <= N - 5;i ++) cnt[i] = 1e18,ans[i] = 1e18;
    for(int i = 1;i <= n;i ++) {
        scanf("%lld",&a[i]);
        Get(a[i]);
    }
    int anst = 1;
    for(int i = 2;i <= N - 5;i ++){
        if(tmp[i] == n) {
            for(int j = 1;j <= ans[i];j ++) anst *= i; 
        }  else if(tmp[i] == n - 1){
            for(int j = 1;j <= cnt[i];j ++) anst *= i; 
        }
    }
    printf("%lld\n",anst);
    return 0;
}

::::

Flatten

给定数组 a_1,a_2,\dots,a_n,求满足以下条件的最小的 \sum_{i=1}^n b_i:

  • 对于任何满足 1\le i<j\le n 的 i,j 都满足 a_ib_i=a_jb_j。

题目给的限制条件其实就是:

a_1b_1=a_2b_2=\dots=a_ib_i=\dots=a_nb_n

那么我们考虑对于一个 b_i,其满足

b_i=\frac{a_1b_1}{a_i}=\frac{a_2b_2}{a_i}=\dots=\frac{a_nb_n}{a_i}

那么我们可以得到,对于任意一个 b_j,它一定同时是 a_1,a_2,\dots,a_{j-1},a_{j+1},\dots,a_n 的倍数。进一步,a_jb_j 一定是 a_1,a_2,\dots,a_n 的倍数。那么 a_jb_j 的最小值就是 \text{lcm}\{a_1,a_2,\dots,a_n\}。由于 a_j 是定值,所以

\boxed{b_j=\frac{\text{lcm}\{a_1,a_2,\dots,a_n\}}{a_j}}

::::success[abc152_e]

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N = 1e6 + 5;
const int Mod = 1e9 + 7; 
int a[N],b[N],n;

int cnt[N];
void Get(int x){
    for(int i = 2;i * i <= x;i ++){
        if(!(x % i)) {
            int sum = 0;
            while(!(x % i)) x /= i,sum ++;
            cnt[i] = max(cnt[i],sum);
        }
    }
    cnt[x] = max(cnt[x],1ll);
} 
int ksm(int a,int b){
    int res = 1;
    while(b){
        if(b & 1) res = res * a % Mod;
        a = a * a % Mod; 
        b >>= 1;
    }
    return res;
}
signed main() {
    scanf("%lld",&n);
    for(int i = 1;i <= n;i ++) scanf("%lld",&a[i]),Get(a[i]);
    int L = 1;
    for(int i = 2;i <= N - 5;i ++) {
        L = (L * ksm(i,cnt[i])) % Mod;
    }
    int ans = 0;
    for(int i = 1;i <= n;i ++) {
        b[i] = L * ksm(a[i],Mod - 2) % Mod;
        ans = (ans + b[i]) % Mod;
    }
    printf("%lld\n",ans);
    return 0;
}

::::

Two Divisors

给定 n 个数 a_1,a_2,\dots,a_n,对于每一个 a_i 找到两个约数 d_1>1 和 d_2>1,使得

\gcd(d_1+d_2,a_i)=1

或者说明不存在。

首先,我们容易证明当 a,b 互质的时候,a\times b 与 a+b 互质。 ::::info[证明]{open} 反证法。\ 假设 a\times b 与 a+b 不互质,即设

\gcd(a\times b,a + b)=p\quad(p\ne 1)

容易得到

\begin{cases}p\mid (a\times b) \\ p\mid (a+b)\end{cases}

所以我们可以得到 p\mid a 或者 p\mid b。\ 当 p\mid a 的时候,由于 p\mid(a+b),所以 p\mid b。所以 \gcd(a,b)\ge p>1,与 a,b 互质的条件相背。p\mid b 同理。\ 所以 a\times b 与 a+b 在 a,b 互质的情况下互质。 ::::

我们设 a_i=p_1^{e_1}p_2^{e_2}\dots p_d^{e_d}(其中 p 数组互不相等),设 d_1=p_1^{e_1},d_2=\frac{a_i}{p_1^{e_1}},此时 d_1 与 d_2 互质,于是 d_1+d_2 与 d_1\times d_2=a_i 互质,满足条件。\ 注意上面假设 d\ge 2,但是事实上可能有 d\le 1,此时由于凑不出来上面的情况(要求 d_1,d_2>1)所以无解\ 但是直接找质因数无疑是超时的。那么我们埃氏筛(也可以是别的筛)出最小的质因数即可。

::::success[CF1366D]

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N = 5e5 + 5;
const int M = 1e7 + 5;
int n;
int a[N],ans[N],tmp[N],d1[N],d2[N],at[N];
int spf[M];

void init() {
    for(int i = 2;i <= M - 5;i ++) {
        if(spf[i]) continue;
        spf[i] = i;
        if(i * i > M - 5) continue;
        for(int j = i * i;j <= M - 5;j += i){
            spf[j] = (spf[j] ? spf[j] : i);
        }
    }
} 

signed main() {
    init();
    scanf("%lld",&n);
    for(int i = 1;i <= n;i ++) {
        scanf("%lld",&a[i]);
        int x = a[i];
        d1[i] = 1; 
        while(!(x % spf[a[i]])) x /= spf[a[i]],d1[i] *= spf[a[i]];
        d2[i] = a[i] / d1[i];
        if(d2[i] == 1){
            d1[i] = d2[i] = -1;
        }
    }
    for(int i = 1;i <= n;i ++) printf("%lld ",d1[i]);
    puts("");
    for(int i = 1;i <= n;i ++) printf("%lld ",d2[i]);
    return 0;
} 

::::

素数密度

给定 L,R,计算 [L,R] 中质数的个数(1\le L\le R<2^{31},R-L\le 10^6)

直接线性筛时间复杂度是 O(R) 的,并且空间也吃不消。\ 由于我们知道任意一个合数 x=a\times b,那么 \min\{a,b\}\le \sqrt{x}。由于我们的线性筛是让一个数被他的最小质因数筛掉,所以实际上我们可以把线性筛分成两步:

这样的话时间复杂度就是 O(\sqrt{R}+R-L+1)。

::::success[P1835]

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int M = 1e7 + 5;
int l,r,ans,txk;
int spf[M];

void init(int tx) {
    for(int i = 2;i <= tx;i ++) {
        if(spf[i]) continue;
        spf[i] = i;
        for(int j = i * i;j <= tx;j += i) if(!spf[j]) spf[j] = i;
    }

    for(int p = 2;p <= tx;p ++) {
        if(spf[p] != p) continue;
        int st = max(p * p,(l + p - 1) / p * p);
        for(int j = st;j <= r;j += p) {
            if(!spf[j + txk]) spf[j + txk] = p;
        }                         
    }
}

signed main() {
    scanf("%lld%lld",&l,&r);
    int tx = sqrt(r);
    while((tx + 1) * (tx + 1) <= r) tx ++;
    while(tx * tx > r) tx --;

    txk = tx + 1 - l;
    init(tx);

    ans = 0;
    for(int i = l;i <= r;i ++) {
        if(i == 1) continue;
        if(!spf[i + txk]) ans ++;
    }
    printf("%lld\n",ans);
    return 0;
}

::::

Sum of gcd of Tuples

对于所有的满足 1\le a_i\le k 的长度为 n 的序列 \{a_1,a_2,\dots,a_n\},计算所有的 n^k 种排列方式的 \gcd\{a_1,a_2,\dots,a_n\} 的和。

我们首先发现,对于一种满足 x\mid\gcd\{a_1,a_2,\dots,a_n\} 的数对,x 会贡献 x\times (\lfloor \frac{k}{x} \rfloor)^n。 ::::info[为什么]{open} 因为我们考虑在满足条件的 a 数组集体 a\to \frac{a}{x} 剩余的值都在值域 [1,\lfloor \frac{k}{x} \rfloor] 排列的方案数是 (\lfloor \frac{k}{x} \rfloor)^n,一种序列贡献 x,所以就是 x\times (\lfloor \frac{k}{x} \rfloor)^n。 :::: 这些序列的实际 \gcd 不一定是 x,而是 x 的某个倍数。所以这样做会有重复。\ 我们需要调整权重为 w(x),使得对于任意实际 \gcd=g 的序列被所有 x\mid g 的项统计之后总权重恰好是 g,也就是

\sum_{x\mid g}w(x)=g

由莫比乌斯反演可知,满足该式的函数 w 唯一,即欧拉函数 \varphi(x)。

所以最后的答案就是

\sum_{x=1}^k\varphi(x)\left\lfloor \frac{k}{x} \right\rfloor^n \pmod {10^9+7}

::::info[什么是欧拉函数?]{open} 欧拉函数 \varphi(n) 表示 1\sim n 中与 n 互质的正整数个数。对于质数的幂,若 p 为质数,则

\varphi(p^k)=p^k-p^{k-1}=p^{k-1}(p-1)

因为在 1\sim p^k 中,只有 p 的倍数不和 p^k 互质,这样的数有 p^{k-1} 个。\ 欧拉函数是积性函数,即

\varphi(m\times n)=\varphi(m)\times \varphi(n)\quad(\gcd(m,n)=1)

通项公式

结论:若 n=p_1^{a_1}p_2^{a_2}\dots p_k^{a_k} 那么有

\varphi(n)=n\prod_{i=1}^k\left(1-\frac{1}{p_i}\right)

证明:容斥原理。一个数 x 与 n 不互质,当且仅当它被 n 的某个质因子 p_i 整除。设 A_i=\{x\in[1,n]:p_i\mid x\} 那么我们可以得到

\varphi(n)=n-\left|\bigcup_{i=1}^k A_i\right|

依靠容斥原理进行展开:

\left|\bigcup_{i=1}^k A_i\right|=\sum_i|A_i|-\sum_{i<j}|A_i\cap A_j|+\sum_{i<j<k}|A_i\cap A_j\cap A_k|-\dots

而 A_i 就是 p_i 的倍数,A_i\cap A_j 就是 p_ip_j 的倍数以此类推

|A_i|=\frac{n}{p_i}\\ |A_i\cap A_j|=\frac{n}{p_ip_j}\\ |A_i\cap A_j\cap A_k|=\frac{n}{p_ip_jp_k}\\ \dots

那么我们可以得到

\varphi(n)=n-\sum_i\frac{n}{p_i}+\sum_{i<j}\frac{n}{p_ip_j}-\sum_{i<j<k}\frac{n}{p_ip_jp_k}+\dots

提出 n 之后第一项后面的项恰好是 \prod_{i=1}^k\left(1-\frac{1}{p_i}\right) 的展开式,可以得到

\varphi(n)=n\prod_{i=1}^k\left(1-\frac{1}{p_i}\right)

因子和恒等式

结论:

\boxed{\sum_{d\mid n}\varphi(d)=n}

证明:令 S=\{1,2,\dots,n\}。对于每一个 k\in S,将分数 \frac{k}{n} 化为最简分数,设约分后为最简分数 \frac{a}{d},那么有

\gcd(a,d)=1\\ 1\le a\le d\\ d\mid n \end{cases}

反过来,给定满足上述条件的数对 (a,d),取 k=\frac{a\times n}{d},则 1\le k\le n 且 \frac{k}{n} 约分后恰为 \frac{a}{d}。于是 k\in S 与满足上述条件的数对 (a,d) 之间建立了一一对应。

把所有 n 个分数按约分后的分母分类:

因此,分数的总数等于各组个数之和:

n=\sum_{d\mid n}\varphi(d)
得证。

::::success[abc162_e]

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N = 1e5 + 5;
const int Mod = 1e9 + 7; 
int n,k,ans=0,phi[N];

int ksm(int a,int b){
    int res = 1;
    while(b){
        if(b & 1) res = res * a % Mod;
        a = a * a % Mod; 
        b >>= 1;
    }
    return res;
}
void init() {
    for(int i = 1;i <= k;i ++) phi[i] = i;
    for(int i = 2;i <= k;i ++) {
        if(phi[i] == i) {
            for(int j = i;j <= k;j += i) {
                phi[j] -= phi[j] / i;
            }
        }
    }
}
signed main() {
    scanf("%lld%lld",&n,&k);
    init();
    for(int i = 1;i <= k;i ++){
        ans = (ans + phi[i] * ksm(k / i,n) % Mod) % Mod; 
    }
    printf("%lld\n",ans);
    return 0;
}

::::

Divide Both

找到满足下列条件的整数对 (x,y) 的数量:

我们分为两个部分,先统计所有不互质的有序对记作 A,然后从 A 中扣除成倍数关系的有序对。\ 我们设 c_d 是区间 [L,R] 中 d 的倍数个数,那么

c_d=\left\lfloor\frac{R}{d}\right\rfloor-\left\lfloor \frac{L-1}{d}\right\rfloor

那么 c_d^2 就是两个数都被 d 整除的有序对的个数。设 f(d) 为恰好 \gcd(x,y)=d 的有序对个数。由于直接用 c_d^2 会重复,因为它包含了 \gcd 是 d 的倍数的数对,需要减去倍数,可以容斥

f(d)=c_d^2-\sum_{j=2}^{\lfloor R/d\rfloor}f(j\times d)

倒序即可。\ 根据定义,我们不难得到 A=\sum_{d=2}^R f(d)。\ 我们只需要考虑所有 d\in [\max(L,2),R] ::::info[理由]{open}

$d<L$ 的时候 $d$ 不在 $[L,R]$ 内不可能作为 $x,y$。 :::: 然后我们具体的看扣除量。\ 所有形如 $(d,y)$ 且 $d \mid y$ 的数对(即 $x \mid y$ 方向): - $y$ 必须是 $d$ 的倍数且 $y \in [L,R]$。 - 因为 $d \ge L$,所以 $\left\lfloor \frac{L-1}{d} \right\rfloor = 0$,个数就是 $\left\lfloor \frac{R}{d} \right\rfloor$。 同理,$y \mid x$ 方向也有 $\left\lfloor \frac{R}{d} \right\rfloor$ 个。两个方向共 $2\times \left\lfloor \frac{R}{d}\right\rfloor$ 个。\ 但数对 $(d,d)$ 同时属于两个方向,被扣了两次,需要加回一次。所以总扣除量是 $$\sum_{d=\max(L,2)}^R\left(2\times \left\lfloor \frac{R}{d} \right\rfloor-1\right)$$ 最后的答案就是 $$Ans = \sum_{d=2}^R f(d)-\sum_{d=\max(L,2)}^R\left(2\times \left\lfloor \frac{R}{d} \right\rfloor-1\right)$$ ::::success[abc206_e] ```c++ #include<bits/stdc++.h> #define int long long using namespace std; const int N = 1e6 + 5; int L,R; int f[N],ans1,ans2; signed main() { scanf("%lld%lld",&L,&R); for(int i = R;i >= 2;i --) { int sum = 0; for(int j = 2;j <= R / i;j ++) sum += f[j * i]; f[i] = (R / i - (L - 1) / i) * (R / i - (L - 1) / i) - sum; } for(int i = 2;i <= R;i ++) ans1 += f[i]; if(max(L,2ll) <= R) { for(int i = max(L,2ll);i <= R;i ++) ans2 += (2ll * (R / i) - 1); } printf("%lld\n",ans1 - ans2); return 0; } ``` :::: ## [CGCDSSQ](https://codeforces.com/problemset/problem/475/D) > 给定长为 $n$ 的正整数序列 $a$,共 $q$ 次询问,每一次给定 $x$ 求满足 > $$\gcd\{a_l,a_{l+1},\dots,a_r\}=x$$ > 的区间 $[l,r]$ 个数。 我们考虑固定右端点,对于 $1\le l\le r$,我们让 $l$ 从 $r$ 出发一直减到 $1$。其有三个性质: - 单调性,$l$ 从右边往左边移动,区间变大,$\gcd$ 单调不增。 - 整除性,$\gcd$ 严格变小时,新值一定是旧值的因子。 - 折半性,每一次 $\gcd$ 严格变化一定满足 $\text{new gcd}\le \frac{\text{old gcd}}{2}$。 ::::info[三种性质的证明]{open} 下文设计算完 $l$ 之后得到的新的 $\gcd$ 值为 $x$,之前是 $y$(即 $y$ 是 $[l+1,r]$ 的 $\gcd$,$x=\gcd(a_l,y)$)。\ 性质一:\ 由于 $\gcd(a,b)\le \min(a,b)$,那么 $x=\gcd(a_l,y)\le y$。即新值不超过旧值。\ 性质二:\ $x=\gcd(a_l,y)$ 一定是 $y$ 的因子,故新值整除旧值。\ 性质三:\ 若 $x<y$,则 $y=ax$,其中 $a\ge 2$ 是损失的因子之乘积,故 $x\le y/2$。 :::: 由于 $a_r\le 10^9$,不同的 $\gcd$ 最多三十种。\ 我们设计对于每一个右端点 $r$,使用链表存储 $$\text{list}=[(g_1,c_1),(g_2,c_2),\dots,(g_k,c_k)]$$ 每一组 $(g_i,c_i)$ 表示有 $c_i$ 个不同的 $l$,使得 $\gcd\{a_l,a_{l+1},\dots,a_r\}=g_i$,那么我们把 $g$ 按照从大到小排列,容易得到相同值一定相邻。那么我们进行分析。\ 以 $r+1$ 为结尾的区间 $[l,r+1]$ 分为两类: - 单元素 $l=r+1$,$\gcd=a_{r+1}$。 - 多元素 $l< r+1$ 考虑集成,我们可以得到 $$\gcd(a_l,a_{l+1},\dots,a_{r+1})=\gcd(\gcd(a_l,a_{l+1},\dots,a_{r}),a_{r+1})$$ 其中 $\gcd(a_l,a_{l+1},\dots,a_{r})$ 已经在表里了。 那么我们考虑先插入单元素 $(a_{r+1},1)$(或者被合并),随后进行挨个的映射,最后合并相邻相同项。时间复杂度 $O(n\log V)$。 ::::success[CF475D] ```c++ #include<bits/stdc++.h> #define int long long using namespace std; const int N = 1e5 + 5; int n,q; int a[N]; unordered_map<int,int> cnt; signed main() { scanf("%lld",&n); for(int i = 1;i <= n;i ++) scanf("%lld",&a[i]); vector<pair<int,int> > lst; for(int i = 1;i <= n;i ++) { vector<pair<int,int> > nxt; nxt.push_back({a[i],1}); for(int j = 0;j < lst.size();j ++) { int g = __gcd(lst[j].first,a[i]); if(g == nxt.back().first) nxt.back().second += lst[j].second; else nxt.push_back({g,lst[j].second}); } for(int j = 0;j < nxt.size();j ++) cnt[nxt[j].first] += nxt[j].second; lst = nxt; } scanf("%lld",&q); while(q --) { int x; scanf("%lld",&x); printf("%lld\n",cnt[x]); } return 0; } ``` :::: ## [Problem b](https://www.luogu.com.cn/problem/P2522) ::::info[前置知识]{open} # 莫比乌斯函数 我们设正整数 $n$ 的质因数分解为 $n=p_1^{a_1}p_2^{a_2}\dots p_{s}^{a_s}$。\ 其中 $p$ 互不相同,$a_i\ge 1$,我们定义: $$ \mu(n)=\begin{cases}0, & \exists i,\ a_i\ge 2\\(-1)^s, & \forall i,\ a_i=1\end{cases}$$ 并补充规定 $\mu(1)=1$。 ## 定理与推论 **定理**:固定 $n$ 则 $$\sum_{d\mid n} \mu(d)=[n=1]$$ **证明**: - 当 $n=1$ 的时候,正因子只有一,那么原式等于 $\mu(1)=1$。 - 当 $n>1$ 的时候,设 $n=p_1^{a_1}p_2^{a_2}\dots p_{s}^{a_s}$,$s\ge 1$ 且 $p_i$ 互不相同。那么其任意一个正因子 $d$ 可以写成 $d=p_1^{b_1}p_2^{b_2}\dots p_{s}^{b_s}\ (0\le b_i\le a_i)$。如果存在 $b_i\ge 2$ 那么直接 $\mu(d)=0$ 对求和无贡献。否则,$d$ 与集合 $\{1,2,\dots,s\}$ 的子集 $S$ 有 $d=\prod_{i\in S}p_i$ 的对应关系,所以 $\mu(d)=(-1)^{|S|}$。容易得到 $$\sum_{d\mid n} \mu(d)=\sum_{S\subseteq\{1,2,\dots,s\}} (-1)^{|S|}=\sum_{k=0}^s\binom{s}{k}(-1)^k$$ 由二项式定理可得 $\sum_{d\mid n} \mu(d)=(1-1)^s=0$。 **推论1**: $$[\gcd(i,j)=1]=\sum_{d\mid\gcd(i,j)} \mu(d)$$ **证明**:\ 令 $n=\gcd(i,j)$,由定理即得。\ **推论2**: $$[\gcd(i,j)=1]=\sum_{d\mid i,\ d\mid j}\mu(d)$$ **证明**:\ 由 $d\mid\gcd(i,j)\iff d\mid i$ 且 $d\mid j$ 即得。 ## 莫比乌斯反演 **定理**:设 $f,g$ 是定义在正整数集合上的函数,满足 $$f(n)=\sum_{d\mid n}g(d)$$ 那么 $$g(n)=\sum_{d\mid n}\mu(d)f\left(\frac{n}{d}\right)$$ **证明**:\ 带入容易得到 $$\sum_{d\mid n}\mu(d)f\left(\frac{n}{d}\right)=\sum_{d\mid n}\mu(d)\sum_{e\mid \frac{n}{d}}g(e)$$ 由 $e\mid\frac{n}{d}\iff d\times e\mid n$ 可以得到 $$\sum_{d\mid n}\sum_{e\mid\frac{n}{d}}\mu(d)g(e)=\sum_{d\times e\mid n}\mu(d)g(e)$$ 交换求和顺序可以得到 $$\sum_{d\times e\mid n}\mu(d)g(e)=\sum_{e\mid n}g(e)\sum_{d\mid\frac{n}{e}}\mu(d)$$ 由定理得到 $$\sum_{d\mid\frac{n}{e}}\mu(d)=\left[\frac{n}{e}=1\right]=[e=n]$$ 容易得到 $$\sum_{e\mid n}g(e)[e=n]=g(n)$$ 得证。 ## 互质计数公式 记 $$H(n,m)=\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=1]$$ 由推论2可以得到 $$H(n,m)=\sum_{i=1}^n\sum_{j=1}^m\sum_{d\mid i,\ d\mid j}\mu(d)$$ 交换求和顺序我们就可以得到: $$ \boxed{H(n,m)=\sum_{d=1}^{\min(n,m)}\mu(d)\left\lfloor\frac{n}{d}\right\rfloor\left\lfloor\frac{m}{d}\right\rfloor} $$ ## 数论分块 容易得到,对于正整数 $n$,$\left\lfloor\frac{n}{t}\right\rfloor$ 作为 $t$ 的函数,只有 $O(\sqrt{n})$ 种不同的取值。\ 下面给定一个问题: > 给定 $l$ 与 $n$,求最大的 $r\ge l$,使得 $\left\lfloor\frac{n}{t}\right\rfloor$ 在 $t\in[l,r]$ 上恒为定值。 设 $k=\left\lfloor\frac{n}{l}\right\rfloor$,条件 $\left\lfloor\frac{n}{t}\right\rfloor=k$ 等价于 $k\le \frac{n}{t}<k+1$,那么 $$\text{Left:}\ k\times t\le n\to t\le \frac{n}{k}$$ $$\text{Right:}\ n<(k+1)\times t\to t> \frac{n}{k+1}$$ 那么 $t$ 的取值区间是 $\left(\frac{n}{k+1},\frac{n}{k}\right]$,最大整数就是 $\left\lfloor\frac{n}{k}\right\rfloor$,那么我们可以得到 $$\boxed{r=\left\lfloor\frac{n}{\left\lfloor\frac{n}{l}\right\rfloor}\right\rfloor}$$ *双变量呢?*\ 如果同时要求 $\left\lfloor\frac{n}{t}\right\rfloor$ 与 $\left\lfloor\frac{m}{t}\right\rfloor$ 不变,那么取两者右端点的较小值, $$r=\min\left(\left\lfloor\frac{n}{\left\lfloor\frac{n}{l}\right\rfloor}\right\rfloor,\left\lfloor\frac{m}{\left\lfloor\frac{m}{l}\right\rfloor}\right\rfloor\right)$$ 那么我们可以得到 $$\sum_{t=l}^r \mu(t)\left\lfloor\frac{n}{t}\right\rfloor\left\lfloor\frac{m}{t}\right\rfloor=\left(\sum_{t=l}^r \mu(t)\right)\left\lfloor\frac{n}{l}\right\rfloor\left\lfloor\frac{m}{l}\right\rfloor$$ 我们可以用前缀和优化,其中 $\sum_{t=l}^r \mu(t)=s_r-s_{l-1}$,$s$ 是 $\mu$ 的前缀和。 :::: 定义 $F(n,m;k)=\sum_{x=1}^n\sum_{y=1}^m[\gcd(x,y)=k]$\ 那么我们根据二维前缀和可以得到 $$\text{Ans}=F(b,d;k)-F(a-1,d;k)-F(b,c-1;k)+F(a-1,c-1;k)$$ 那么我们可以得: $$ F(n,m;k)=H\left(\left\lfloor\frac{n}{k}\right\rfloor,\left\lfloor\frac{m}{k}\right\rfloor\right)\\ H(n,m)=\sum_{u=1}^n\sum_{v=1}^m[\gcd(u,v)=1] $$ 那么我们根据上面折叠筐内的内容容易得到 $$H(n,m)=\sum_{d=1}^{\min(n,m)}\mu(d)\left\lfloor\frac{n}{d}\right\rfloor\left\lfloor\frac{m}{d}\right\rfloor$$ 考虑数论分块优化。 ::::success[P2522] ```c++ #include<bits/stdc++.h> #define int long long using namespace std; const int N = 5e4 + 5; int q; int mu[N],pri[N],vis[N],tot; int s[N]; void init(int n) { mu[1] = 1; for(int i = 2;i <= n;i ++) { if(!vis[i]) { pri[++ tot] = i; mu[i] = -1; } for(int j = 1;j <= tot && i * pri[j] <= n;j ++) { vis[i * pri[j]] = 1; if(i % pri[j] == 0) { mu[i * pri[j]] = 0; break; } mu[i * pri[j]] = -mu[i]; } } for(int i = 1;i <= n;i ++) s[i] = s[i - 1] + mu[i]; } int H(int n,int m) { if(n <= 0 || m <= 0) return 0; int res = 0; for(int l = 1,r;l <= min(n,m);l = r + 1) { r = min(n / (n / l),m / (m / l)); res += (s[r] - s[l - 1]) * (n / l) * (m / l); } return res; } int F(int n,int m,int k) { return H(n / k,m / k); } signed main() { scanf("%lld",&q); init(N-5); while(q --) { int a,b,c,d,k; scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k); int ans = F(b,d,k) - F(a - 1,d,k) - F(b,c - 1,k) + F(a - 1,c - 1,k); printf("%lld\n",ans); } return 0; } ``` :::: ## [[SDOI2015] 约数个数和](https://www.luogu.com.cn/problem/P3327) > 设 $d(x)$ 表示 $x$ 的约数个数,给定 $n,m$ 求 > $$\sum_{i=1}^n\sum_{j=1}^md(i\times j)$$ 首先我们可以得到 $$\boxed{d(i\times j)=\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1]}$$ **证明**:我们先考虑*只有一个质数*的情况:\ 设 $i=p^a,j=p^b$,那么我们可以得到 $$ \begin{cases} i\times j=p^{a+b}\\ d(i\times j)=a+b+1 \end{cases} $$ 那么我们由 $x|i,y|j$ 可以得到 $x=p^u(0\le u\le a),y=p^v(0\le v\le b)$\ *我们设 $u=0,v=0$ $\to$ $\gcd(p^u,p^v)=1$ 那么我们需要计算 $(u,v)$ 这样的数对的个数*。\ 那么我们分类讨论: - $u=0$,$v$ 可以取 $0\sim b$,共 $b+1$ 个。 - $v=0$,$u$ 可以取 $0\sim a$,由于 $u=0$ 已经数过了,所以由 $a$ 个。 由于 $b+1+a=a+b+1=d(i\times j)$ 得证。\ 扩展到*多个质数*。\ 我们不妨设 $i=\prod_{k=1}^tp_k^{a_k}$,$j=\prod_{k=1}^tp_k^{b_k}$。\ *其中 $p_1,p_2,\dots,p_t$ 是 $i\times j$ 的所有不同质因子,允许某些 $a_k,b_k=0$。*\ 由于 $i\times j=\prod_{k=1}^tp_k^{a_k+b_k}$,所以可以得到 $$d(i\times j)=\prod_{k=1}^t(a_k+b_k+1)$$ ::::info[补充]{open} 因为如果正整数 $n$ 的质因数分解为 $$n=p_1^{e_1}p_2^{e_2}\dots p_t^{e_t}$$ 那么 $n$ 的正约数的个数是: $$d(n)=\prod_{k=1}^t(e_k+1)$$ :::: 那么我们进行带入: $$\sum_{i=1}^n\sum_{j=1}^md(i\times j)=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1]$$ 然后交换求和顺序,问题变成 > 固定 $x,y$ 求多少个 $i,j\in[1,n]$ 满足 $x|i,y|j$? 显然,是 $\lfloor\frac{n}{x}\rfloor$ 和 $\lfloor\frac{m}{y}\rfloor$ 所以原式变为: $$\sum_{x=1}^n\sum_{y=1}^m\lfloor\frac{n}{x}\rfloor\lfloor\frac{m}{y}\rfloor[\gcd(x,y)=1]$$ 莫比乌斯反演容易得到: $$[\gcd(x,y)=1]=\sum_{d|\gcd(x,y)}\mu(d)=\sum_{d|x,d|y}\mu(d)$$ 那么进行带入: $$\sum_{x=1}^n\sum_{y=1}^m\lfloor\frac{n}{x}\rfloor\lfloor\frac{m}{y}\rfloor=\sum_{d|x,d|y}\mu(d)$$ 随后进行交换求和。固定 $d$,$x$ 必须是 $d$ 的倍数,我们设: - $x=dx'$,得到 $x'\le \lfloor\frac{n}{d}\rfloor$。 - $y=dy'$,得到 $y'\le \lfloor\frac{m}{d}\rfloor$。 那么我们就会得到下面这个式子 $$\sum_{d=1}^{\min(n,m)}\mu(d)\sum_{x'=1}^{\lfloor\frac{n}{d}\rfloor}\lfloor\frac{n}{dx'}\rfloor\sum_{y'=1}^{\lfloor\frac{m}{d}\rfloor}\lfloor\frac{m}{dy'}\rfloor$$ 我们设 $$F(t)=\sum_{i=1}^t\lfloor\frac{t}{i}\rfloor$$ 那么我们可以得到: $$\boxed{\sum_{d=1}^{\min(n,m)}\mu(d)\times F(\lfloor\frac{n}{d}\rfloor)\times F(\lfloor\frac{m}{d}\rfloor)}$$ 那么我们使用数论分块进行预处理 $F(t)$ 即可。 ::::success[P3327] ```c++ #include<bits/stdc++.h> #define int long long using namespace std; const int N = 5e4 + 5; int T; int mu[N],pri[N],vis[N],tot; int S[N]; int F[N]; void init(int n) { mu[1] = 1; for(int i = 2;i <= n;i ++) { if(!vis[i]) { pri[++ tot] = i; mu[i] = -1; } for(int j = 1;j <= tot && i * pri[j] <= n;j ++) { vis[i * pri[j]] = 1; if(i % pri[j] == 0) { mu[i * pri[j]] = 0; break; } mu[i * pri[j]] = -mu[i]; } } for(int i = 1;i <= n;i ++) S[i] = S[i - 1] + mu[i]; for(int i = 1;i <= n;i ++) { for(int l = 1,r;l <= i;l = r + 1) { r = i / (i / l); F[i] += (r - l + 1) * (i / l); } } } int solve(int n,int m) { if(n > m) swap(n,m); int res = 0; for(int l = 1,r;l <= n;l = r + 1) { r = min(n / (n / l),m / (m / l)); res += (S[r] - S[l - 1]) * F[n / l] * F[m / l]; } return res; } signed main() { scanf("%lld",&T); init(50000); while(T --) { int n,m; scanf("%lld%lld",&n,&m); printf("%lld\n",solve(n,m)); } return 0; } ``` ::::