题目
从区间 [ l ∼ r ] [l\sim r] [l∼r]中选取 n n n个整数,总共有 ( r − l + 1 ) n (r-l+1)^n (r−l+1)n种方案。问最大公约数刚好为 k k k的选取方案有多少个。
分析
那么也就是在求
[
⌈
l
k
⌉
∼
⌊
r
k
⌋
]
中
选
取
[\lceil\frac{l}{k}\rceil\sim\lfloor\frac{r}{k}\rfloor]中选取
[⌈kl⌉∼⌊kr⌋]中选取n
个
整
数
,
最
大
公
约
数
刚
好
为
1
的
选
取
方
案
数
个整数,最大公约数刚好为1的选取方案数
个整数,最大公约数刚好为1的选取方案数
那么之后怎么做呢,因为
r
−
l
r-l
r−l比较小,所以说可以选择枚举因数
i
i
i,那么缩小的范围就变为
[
⌈
⌈
l
k
⌉
i
⌉
∼
⌊
⌊
r
k
⌋
i
⌋
]
[\lceil\frac{\lceil\frac{l}{k}\rceil}{i}\rceil\sim\lfloor\frac{\lfloor\frac{r}{k}\rfloor}{i}\rfloor]
[⌈i⌈kl⌉⌉∼⌊i⌊kr⌋⌋],那么其中的数就是对于因数为
i
i
i时,范围内除以
i
i
i的数,设
d
p
[
i
]
dp[i]
dp[i]为选取的数公因数是
i
i
i的个数,那么
d
p
[
i
]
=
(
⌊
⌊
r
k
⌋
i
⌋
−
⌈
⌈
l
k
⌉
i
⌉
+
1
)
n
−
(
⌊
⌊
r
k
⌋
i
⌋
−
⌈
⌈
l
k
⌉
i
⌉
+
1
)
dp[i]=(\lfloor\frac{\lfloor\frac{r}{k}\rfloor}{i}\rfloor-\lceil\frac{\lceil\frac{l}{k}\rceil}{i}\rceil+1)^n-(\lfloor\frac{\lfloor\frac{r}{k}\rfloor}{i}\rfloor-\lceil\frac{\lceil\frac{l}{k}\rceil}{i}\rceil+1)
dp[i]=(⌊i⌊kr⌋⌋−⌈i⌈kl⌉⌉+1)n−(⌊i⌊kr⌋⌋−⌈i⌈kl⌉⌉+1)
那问题是我们求的是1,那可以干什么呢,我们枚举i的倍数,
d
p
[
i
]
−
=
∑
j
=
2
x
d
p
[
i
j
]
dp[i]-=\sum_{j=2}^{x}dp[ij]
dp[i]−=∑j=2xdp[ij],这样就可以求出最大公因数是
i
i
i的个数,时间复杂度
O
(
2
×
(
r
−
l
)
log
2
(
r
−
l
)
)
O(2\times (r-l)\log_2(r-l))
O(2×(r−l)log2(r−l))
代码
#include <cstdio>
#define rr register
using namespace std;
const int mod=(int)1e9+7;
int n,k,l,r,dp[100001];
inline signed ksm(long long x,int y){
rr long long ans=1;
while (y){
if (y&1) (ans*=x)%=mod;
(x*=x)%=mod; y>>=1;
}
return ans;
}
signed main(){
scanf("%d%d%d%d",&n,&k,&l,&r);
l=l/k+(l%k>0),r/=k;
if (l>r) return !putchar(48);
for (rr int i=1;i<=r-l;++i){
rr int L=l/i+(l%i>0),R=r/i;
if (L>R) break;
dp[i]=(ksm(R-L+1,n)-R+L-1+mod)%mod;
}
for (rr int i=r-l;i;--i)
for (rr int j=2;i*j<=r-l;++j)
(dp[i]-=dp[i*j]-mod)%=mod;
if (l==1) (++dp[1])%=mod;//如果最后扩散到的范围在1上,那么1要多加
return !printf("%d",dp[1]);
}