我们知道,从区间 [ L , H ] [L,H] [L,H]( L L L和 H H H为整数)中选取 N N N个整数,总共有 ( H − L + 1 ) N (H-L+1)^N (H−L+1)N种方案。小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的 N N N个整数都求一次最大公约数,以便进一步研究。然而他很快发现工作量太大了,于是向你寻求帮助。你的任务很简单,小z会告诉你一个整数 K K K,你需要回答他最大公约数刚好为 K K K的选取方案有多少个。由于方案数较大,你只需要输出其除以 1000000007 1000000007 1000000007的余数即可。
输入一行,包含 4 4 4个空格分开的正整数,依次为 N N N, K K K, L L L和 H H H。
输出一个整数,为所求方案数。
2 2 2 4
3
样例解释
所有可能的选择方案: ( 2 , 2 ) , ( 2 , 3 ) , ( 2 , 4 ) , ( 3 , 2 ) , ( 3 , 3 ) , ( 3 , 4 ) , ( 4 , 2 ) , ( 4 , 3 ) , ( 4 , 4 ) (2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2), (4, 3), (4, 4) (2,2),(2,3),(2,4),(3,2),(3,3),(3,4),(4,2),(4,3),(4,4)
其中最大公约数等于 2 2 2的只有 3 3 3组: ( 2 , 2 ) , ( 2 , 4 ) , ( 4 , 2 ) (2, 2), (2, 4), (4, 2) (2,2),(2,4),(4,2)
对于100%的数据, 1 ≤ N , K ≤ 1 0 9 1\le N,K\le 10^9 1≤N,K≤109, 1 ≤ L ≤ H ≤ 1 0 9 1\le L\le H\le 10^9 1≤L≤H≤109, H − L ≤ 1 0 5 H-L\le 10^5 H−L≤105
首先还是很套路地将 L L L变成 ⌈ L K ⌉ \lceil \frac{L}{K}\rceil ⌈KL⌉, H H H变成 ⌊ H K ⌋ \lfloor\frac{H}{K}\rfloor ⌊KH⌋, 那么问题就变成了从 [ L , H ] [L,H] [L,H]选出 N N N个数, 使得其 g c d gcd gcd为 1 1 1。
这里有个很讨厌的问题: L , H L,H L,H可能很大, 但 H − L H-L H−L比较小。 如果我们直接枚举包含约数 i i i的方案数个数, 很可能需要枚举到 1 0 9 10^9 109, 这显然是不可行的。 然后我们发现, 这些情况都只会取 N N N次一个数, 因为如果在 [ L , H ] [L,H] [L,H]中取多个数的话, 它们的 g c d gcd gcd不会超过 1 0 5 10^5 105, 而这种约数对我们的答案是没有贡献的。
因此, 我们先不考虑只有一种数的情况, 那么我们包含的约数 i i i只需要枚举到 H − L H-L H−L。 然后这样的方案数为 c n t i N − c n t i cnt_i^N-cnt_i cntiN−cnti。 最后从大到小容斥一遍即可。
如果 L = 1 L=1 L=1, 那么实际上我们是可以全部取到 K K K的, 最后方案数 + 1 +1 +1即可。
代码如下:
#include
#include
#include
#include
#include
#include
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MX 100500
#define MOD 1000000007ll
int dp[MX];
IN int fpow(R int base, R int tim)
{
int ret = 1;
W (tim)
{
if (tim & 1) ret = 1ll * ret * base % MOD;
base = 1ll * base * base % MOD, tim >>= 1;
}
return ret;
}
int main(void)
{
int n, l, h, k, halt, lef, rig;
scanf("%d%d%d%d", &n, &k, &l, &h);
l = std::ceil(1.0 * l / k), h = h / k;
halt = h - l;
if (l > h) return puts("0"), 0;
for (R int i = 1; i <= halt; ++i)
{
lef = std::ceil(1.0 * l / i), rig = h / i;
if (lef > rig) continue;
dp[i] = (fpow((rig - lef + 1), n) - (rig - lef + 1) + MOD) % MOD;
}
for (R int i = halt; i; --i)
{
for (R int j = i << 1; j <= halt; j += i)
dp[i] = (dp[i] - dp[j] + MOD) % MOD;
}
if (l == 1) dp[1] = (dp[1] + 1) % MOD;
printf("%d", dp[1]);
}