牛牛的最大公约数
传送门
题意:一个区间内选N个数(可以重复选),求这N个数的gcd==K的选法数。
这周写了三篇论文猝死预备的我终于来填坑啦(lll¬ω¬)
思路:
首先将要求的用数学公式表达出来: ∑ i 1 = l r . . . ∑ i n = l r [ g c d ( i 1 , . . . , i n ) = k ] \sum_{i_1=l}^{r}...\sum_{i_n=l}^{r}[gcd(i_1,...,i_n)=k] ∑i1=lr...∑in=lr[gcd(i1,...,in)=k]。
等价于
∑
i
1
=
⌈
l
k
⌉
⌊
r
k
⌋
.
.
.
∑
i
n
=
⌈
l
k
⌉
⌊
r
k
⌋
[
g
c
d
(
i
1
,
.
.
.
,
i
n
)
=
1
]
\sum_{i_1=\lceil\frac{l}{k}\rceil}^{\lfloor\frac{r}{k}\rfloor}...\sum_{i_n=\lceil\frac{l}{k}\rceil}^{\lfloor\frac{r}{k}\rfloor}[gcd(i_1,...,i_n)=1]
∑i1=⌈kl⌉⌊kr⌋...∑in=⌈kl⌉⌊kr⌋[gcd(i1,...,in)=1]。
由莫比乌斯函数的性质:
[
g
c
d
(
i
,
j
)
=
1
]
⟺
∑
d
∣
g
c
d
(
i
,
j
)
μ
(
d
)
[gcd(i,j)=1]\iff\sum_{d|gcd(i,j)}\mu(d)
[gcd(i,j)=1]⟺∑d∣gcd(i,j)μ(d)
原式等价于
∑
i
1
=
⌈
l
k
⌉
⌊
r
k
⌋
.
.
.
∑
i
n
=
⌈
l
k
⌉
⌊
r
k
⌋
∑
d
∣
g
c
d
(
i
1
,
.
.
.
,
i
n
)
μ
(
d
)
\sum_{i_1=\lceil\frac{l}{k}\rceil}^{\lfloor\frac{r}{k}\rfloor}...\sum_{i_n=\lceil\frac{l}{k}\rceil}^{\lfloor\frac{r}{k}\rfloor}\sum_{d|gcd(i_1,...,i_n)}\mu(d)
∑i1=⌈kl⌉⌊kr⌋...∑in=⌈kl⌉⌊kr⌋∑d∣gcd(i1,...,in)μ(d)
为了清晰,以新的
l
l
l表示原
⌈
l
k
⌉
\lceil\frac{l}{k}\rceil
⌈kl⌉,新
r
r
r表示原
⌊
r
k
⌋
\lfloor\frac{r}{k}\rfloor
⌊kr⌋:
∑
i
1
=
l
r
.
.
.
∑
i
n
=
l
r
∑
d
∣
g
c
d
(
i
1
,
.
.
.
,
i
n
)
μ
(
d
)
\sum_{i_1=l}^{r}...\sum_{i_n=l}^{r}\sum_{d|gcd(i_1,...,i_n)}\mu(d)
∑i1=lr...∑in=lr∑d∣gcd(i1,...,in)μ(d)
变换枚举顺序,原式等价于
∑
d
=
1
r
μ
(
d
)
∑
i
1
=
l
r
.
.
.
∑
i
n
=
l
r
[
d
∣
g
c
d
(
i
1
,
.
.
.
i
n
)
]
\sum_{d=1}^{r}\mu(d)\sum_{i_1=l}^{r}...\sum_{i_n=l}^{r}[d|gcd(i_1,...i_n)]
∑d=1rμ(d)∑i1=lr...∑in=lr[d∣gcd(i1,...in)]
拆开后面的枚举项,等价于
∑
d
=
1
r
μ
(
d
)
∑
i
1
=
l
r
[
d
∣
i
1
]
.
.
.
∑
i
n
=
l
r
[
d
∣
i
n
]
\sum_{d=1}^{r}\mu(d)\sum_{i_1=l}^{r}[d|i_1]...\sum_{i_n=l}^{r}[d|i_n]
∑d=1rμ(d)∑i1=lr[d∣i1]...∑in=lr[d∣in]
在
l
l
l和
r
r
r之间能够被
d
d
d整除的数字有
⌊
r
d
⌋
−
⌊
l
−
1
d
⌋
\lfloor\frac{r}{d}\rfloor-\lfloor\frac{l-1}{d}\rfloor
⌊dr⌋−⌊dl−1⌋个。
因此,原式等价于
∑
d
=
1
r
μ
(
d
)
(
⌊
r
d
⌋
−
⌊
l
−
1
d
⌋
)
n
\sum_{d=1}^{r}\mu(d)(\lfloor\frac{r}{d}\rfloor-\lfloor\frac{l-1}{d}\rfloor)^n
∑d=1rμ(d)(⌊dr⌋−⌊dl−1⌋)n
(
⌊
r
d
⌋
−
⌊
l
−
1
d
⌋
)
n
(\lfloor\frac{r}{d}\rfloor-\lfloor\frac{l-1}{d}\rfloor)^n
(⌊dr⌋−⌊dl−1⌋)n可以借由数论分块求出。
∑
d
=
1
r
μ
(
d
)
\sum_{d=1}^{r}\mu(d)
∑d=1rμ(d)前缀和可以借由杜教筛求出。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,k,l,r;
const ll maxn=1e7;//预处理量
const ll mod=1e9+7;
map<ll,ll> mp_mu;
ll vis[maxn+2],p[maxn],mu[maxn+2],tot=0,mu_sum[maxn+2];
void init(){//预处理
mu[1]=1;
for(int i=2;i<=maxn;i++){
if(!vis[i]) p[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&i*p[j]<=maxn;j++){
vis[i*p[j]]=1;
if(i%p[j]==0){
mu[i*p[j]]=0;
break;
}
mu[i*p[j]]=-mu[i];
}
}
}
ll qpow(ll a,ll n){
ll ans=1;
while(n){
if(n&1) ans=(ans*a)%mod;
a=(a*a)%mod;
n>>=1;
}
return ans;
}
ll getMu(ll x){
if(x<=maxn) return mu_sum[x];
if(mp_mu.find(x)!=mp_mu.end()) return mp_mu[x];
ll res=1ll;
for(ll i=2,j;i<=x;i=j+1){
j=x/(x/i);
res-=getMu(x/i)*(j-i+1);
}
return mp_mu[x]=res;
}
ll du(ll L,ll R){//杜教筛
ll res=0;
for(int i=1,j;i<=R;i=j+1){
if(L-1<i) j=R/(R/i);//防止除零意外
else j=min(R/(R/i),(L-1)/((L-1)/i));//取小的那个:毕竟是分块嘛
ll x=getMu(j)-getMu(i-1);
ll y=qpow(R/i-(L-1)/i,n);
res=(res+(x*y)%mod)%mod;
}
res=(res+mod)%mod;//要是正才行
return res;
}
int main(){
int L,R;
init();
scanf("%lld%lld%d%d",&n,&k,&L,&R);
l=(L+k-1)/k;r=R/k;
for(int i=1;i<=min(r,maxn);i++) mu_sum[i]=mu_sum[i-1]+mu[i];
ll ans=du(l,r);
printf("%lld\n",ans);
}
涉及知识
数论分块
理论:
数论分块用于快速处理形如
∑
i
n
⌊
n
i
⌋
\sum_{i}^{n}\lfloor\frac{n}{i}\rfloor
∑in⌊in⌋的式子。复杂度为
O
(
n
)
O(\sqrt{n})
O(n)(大概)。
通过观察可以发现,对于同一个
n
n
n,
⌊
n
i
⌋
\lfloor\frac{n}{i}\rfloor
⌊in⌋的值随着
i
i
i呈现块状分布。对于任意一个
i
(
i
≤
n
)
i( i\leq n)
i(i≤n),能找到一个最大的
j
(
i
≤
j
≤
n
)
j(i\leq j\leq n)
j(i≤j≤n),使得
⌊
n
i
⌋
=
⌊
n
j
⌋
\lfloor\frac{n}{i}\rfloor=\lfloor\frac{n}{j}\rfloor
⌊in⌋=⌊jn⌋,在这之间的
k
(
i
≤
k
≤
j
)
k(i\leq k\leq j)
k(i≤k≤j)都有
⌊
n
k
⌋
=
⌊
n
i
⌋
\lfloor\frac{n}{k}\rfloor=\lfloor\frac{n}{i}\rfloor
⌊kn⌋=⌊in⌋。
而这个
j
=
⌊
n
⌊
n
i
⌋
⌋
j=\lfloor\frac{n}{\lfloor\frac{n}{i}\rfloor}\rfloor
j=⌊⌊in⌋n⌋。
证明:
⌊
n
i
⌋
≤
n
i
\lfloor\frac{n}{i}\rfloor\leq \frac{n}{i}
⌊in⌋≤in
⟹
⌊
n
⌊
n
i
⌋
⌋
≥
⌊
n
n
i
⌋
=
⌊
i
⌋
=
i
\implies\lfloor\frac{n}{\lfloor\frac{n}{i}\rfloor}\rfloor\geq\lfloor\frac{n}{\frac{n}{i}}\rfloor=\lfloor i\rfloor=i
⟹⌊⌊in⌋n⌋≥⌊inn⌋=⌊i⌋=i
⟹
j
≥
i
\implies j\geq i
⟹j≥i
因此,每次以
[
i
,
j
]
[i,j]
[i,j]为一块,分块求和即可。
实现:
int ans=0;
for(int i=1,j;i<=n;i=j+1){
j=n/(n/i);
ans+=(j-(i-1))*(n/i);
}
Mobius函数(莫比乌斯)
理论:
定义:
μ ( d ) = { 1 d = 1 ( − 1 ) r d = p 1 . . . p r , p 1 , . . . , p r 是两两不同的素数 0 其他,即 d 有大于 1 的平方因数 \mu(d)=\begin{cases} 1&d=1\\{(-1)^r}&{d=p_1...p_r,p_1,...,p_r}\text{是两两不同的素数}\\0&{\text{其他,即}d\text{有大于}1\text{的平方因数}}\end{cases} μ(d)=⎩⎪⎨⎪⎧1(−1)r0d=1d=p1...pr,p1,...,pr是两两不同的素数其他,即d有大于1的平方因数
性质:
(证明见课本)
(1)积性函数(
(
d
1
,
d
2
)
=
1
(d_1,d_2)=1
(d1,d2)=1时
μ
(
d
1
d
2
)
=
μ
(
d
1
)
μ
(
d
2
)
\mu(d_1d_2)=\mu(d_1)\mu(d_2)
μ(d1d2)=μ(d1)μ(d2))
(2)
∑
d
∣
n
μ
(
d
)
=
[
1
n
]
=
{
1
n
=
1
0
n
≠
0
\sum_{d|n}\mu(d)=[\frac{1}{n}]=\begin{cases} 1&{n=1}\\0&{n \neq 0}\end{cases}
∑d∣nμ(d)=[n1]={10n=1n=0
(3)
[
g
c
d
(
i
,
j
)
=
1
]
⟺
∑
d
∣
g
c
d
(
i
,
j
)
μ
(
d
)
[gcd(i,j)=1]\iff\sum_{d|gcd(i,j)}\mu(d)
[gcd(i,j)=1]⟺∑d∣gcd(i,j)μ(d)
*(3)的证明:通过Dirichlet卷积。
筛法:
线性筛
void getMu(){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]) p[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&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];
}
}
}
前缀和筛法:
杜教筛 和 Min_25筛(原理都是因为 μ \mu μ是积性函数)
Dirichlet卷积(狄利克雷)
两个数论函数的Dirichlet卷积是
(
f
∗
g
)
(
n
)
=
∑
d
∣
n
f
(
d
)
g
(
n
d
)
(f\ast g)(n)=\sum_{d|n}f(d)g(\frac{n}{d})
(f∗g)(n)=∑d∣nf(d)g(dn)。满足交换律和结合律。
ε
=
μ
∗
1
⟺
ε
(
n
)
=
∑
d
∣
n
μ
(
d
)
\varepsilon=\mu\ast 1\iff \varepsilon(n)=\sum_{d|n}\mu(d)
ε=μ∗1⟺ε(n)=∑d∣nμ(d)
注:
ε
(
n
)
=
[
n
=
1
]
\varepsilon(n)=[n=1]
ε(n)=[n=1]为单位函数
杜教筛
被用来处理数论函数的前缀和问题。低于线性时间复杂度。
主要思路:通过寻找递推关系的方法来压缩计算步骤。
理论:
对于数论函数
f
(
x
)
f(x)
f(x),要求
S
(
x
)
=
∑
x
=
1
n
f
(
x
)
S(x)=\sum_{x=1}^{n} f(x)
S(x)=∑x=1nf(x),构造
S
(
n
)
S(n)
S(n)关于
S
(
⌊
n
i
⌋
)
S(\lfloor\frac{n}{i}\rfloor)
S(⌊in⌋)的递推式。
对任意一个数论函数
g
g
g,
∑
i
=
1
n
∑
d
∣
i
f
(
d
)
g
(
i
d
)
=
∑
i
=
1
n
g
(
i
)
S
(
⌊
n
i
⌋
)
\sum_{i=1}^n\sum_{d|i}f(d)g(\frac{i}{d})=\sum_{i=1}^{n}g(i)S(\lfloor\frac{n}{i}\rfloor)
∑i=1n∑d∣if(d)g(di)=∑i=1ng(i)S(⌊in⌋)
⟺
∑
i
=
1
n
(
f
∗
g
)
(
i
)
=
∑
i
=
1
n
g
(
i
)
S
(
⌊
n
i
⌋
)
\iff\sum_{i=1}^{n}(f\ast g)(i)=\sum_{i=1}^{n}g(i)S(\lfloor\frac{n}{i}\rfloor)
⟺∑i=1n(f∗g)(i)=∑i=1ng(i)S(⌊in⌋)
因此有
g
(
1
)
S
(
n
)
=
∑
i
=
1
n
(
f
∗
g
)
(
i
)
−
∑
i
=
2
n
g
(
i
)
S
(
⌊
n
i
⌋
)
g(1)S(n)=\sum_{i=1}^{n}(f\ast g)(i)-\sum_{i=2}^{n}g(i)S(\lfloor\frac{n}{i}\rfloor)
g(1)S(n)=∑i=1n(f∗g)(i)−∑i=2ng(i)S(⌊in⌋)
如果我们可以快速求出
∑
i
=
1
n
(
f
∗
g
)
(
i
)
\sum_{i=1}^{n}(f\ast g)(i)
∑i=1n(f∗g)(i),并使用数论分块递归地求出
∑
i
=
2
n
g
(
i
)
S
(
⌊
n
i
⌋
)
\sum_{i=2}^{n}g(i)S(\lfloor\frac{n}{i}\rfloor)
∑i=2ng(i)S(⌊in⌋),那么就能得出
S
(
n
)
S(n)
S(n)。
证明:
∑
i
=
1
n
∑
d
∣
i
f
(
d
)
g
(
i
d
)
\sum_{i=1}^{n}\sum_{d|i}f(d)g(\frac{i}{d})
∑i=1n∑d∣if(d)g(di)
=
∑
i
=
1
n
∑
d
∣
i
g
(
d
)
f
(
i
d
)
=\sum_{i=1}^{n}\sum_{d|i}g(d)f(\frac{i}{d})
=∑i=1n∑d∣ig(d)f(di)(交换律)
=
∑
d
=
1
n
∑
i
d
=
1
⌊
n
i
⌋
g
(
d
)
f
(
⌊
i
d
⌋
)
=\sum_{d=1}^n\sum_{\frac{i}{d}=1}^{\lfloor\frac{n}{i}\rfloor} g(d)f(\lfloor\frac{i}{d}\rfloor)
=∑d=1n∑di=1⌊in⌋g(d)f(⌊di⌋)(意识到原式是对于所有
i
≤
n
i\leq n
i≤n的贡献,根据这个变换枚举顺序)
=
∑
i
=
1
n
∑
j
=
1
⌊
n
i
⌋
g
(
i
)
f
(
j
)
=\sum_{i=1}^n\sum_{j=1}^{\lfloor\frac{n}{i}\rfloor} g(i)f(j)
=∑i=1n∑j=1⌊in⌋g(i)f(j)(分别用新的
i
i
i、
j
j
j代替
d
d
d和
i
d
\frac{i}{d}
di)
实现:
求
μ
\mu
μ的前缀和。
由
ε
=
μ
∗
1
⟺
ε
(
n
)
=
∑
d
∣
n
μ
(
d
)
\varepsilon=\mu\ast 1\iff \varepsilon(n)=\sum_{d|n}\mu(d)
ε=μ∗1⟺ε(n)=∑d∣nμ(d)可知,设
μ
\mu
μ的前缀和为
S
(
n
)
S(n)
S(n),那么
S
(
n
)
=
∑
i
=
1
n
ε
(
i
)
−
∑
i
=
2
n
S
(
⌊
n
i
⌋
)
S(n)=\sum_{i=1}^{n}\varepsilon(i)-\sum_{i=2}^{n}S(\lfloor\frac{n}{i}\rfloor)
S(n)=∑i=1nε(i)−∑i=2nS(⌊in⌋)
=
1
−
∑
i
=
2
n
S
(
⌊
n
i
⌋
)
=1-\sum_{i=2}^{n}S(\lfloor\frac{n}{i}\rfloor)
=1−∑i=2nS(⌊in⌋)
⌊
n
i
⌋
\lfloor\frac{n}{i}\rfloor
⌊in⌋最多只有
O
(
n
)
O(\sqrt n)
O(n)种取值,用数论分块寻找。
直接计算时间复杂度
O
(
n
3
4
)
O(n^{\frac{3}{4}})
O(n43),先用线性筛预处理前
n
2
3
n^{\frac{2}{3}}
n32,剩余部分时间复杂度为
O
(
n
2
3
)
O(n^{\frac{2}{3}})
O(n32)。
ll get_large_mu(ll x){
if(x<n) return sum_mu[x];//预处理(线性筛)计算的
if(mp_mu.find(x)!=mp_mu.end()) return mp_mu[x];//get_large的时候计算过的
ll res=1ll;
for(ll i=2,j;i<=x;i=j+1){//从2开始
j=x/(x/i);
res-=get_large_mu(x/i)*(j-(i-1));//数论分块,递归调用
}
return mp_mu[x]=res;
}