[CQOI2015]选数
嘟嘟嘟
首先问题可以转化一下,变成在\([ \lceil \frac{L}{k} \rceil, \lfloor \frac{R}{k} \rfloor]\)中选取\(n\)个数,使这些数的gcd等于1.
以下的\(L\)和\(R\)都是除完\(k\)的。
但这样做的复杂度是\(O(R)\)的,过不了。
这时候考虑到一个性质,在这个区间中选取不相同的\(n\)个数,这\(n\)个数的gcd一定小于等于他们的极差。
这样我们的枚举范围就变成了\(R - L\)。
令\(dp[i]\)表示从这个区间中选取不完全相同的\(n\)个数,他们的gcd = \(i\)的方案数。
那么自然有\(dp[i] = (\frac{R}{i} - \frac{L - 1}{i}) ^ n - (\frac{R}{i} - \frac{L - 1}{i})\)。
但是这样\(dp[i]\)也包含了gcd为\(2i\),\(3i\)……的情况,所以我们容斥一下,\(i\)从大到小枚举,把\(dp[i]\)依次减去\(dp[2i]\),\(dp[3i]\)……。
有人会问不应该是\(dp[i]\)减去\(dp[2i]\)就行了么。但是别忘了,这时候\(dp[2i]\)已经处理过了,就表示gcd为\(2i\)的方案数,所以应该减去所有\(i\)的倍数。
特殊情况是\(L = 1\),这时候可以都选\(1\),所以要加1.
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 1e5 + 5;
const ll mod = 1e9 + 7;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
In ll quickpow(ll a, ll b)
{
ll ret = 1;
for(; b; b >>= 1, a = a * a % mod)
if(b & 1) ret = ret * a % mod;
return ret;
}
ll dp[maxn];
int main()
{
int n = read(), k = read(), L = read(), R = read();
L = (L + k - 1) / k, R /= k;
for(int i = 1; i <= R - L; ++i)
{
int num = R / i - (L - 1) / i;
dp[i] = (quickpow(num, n) - num + mod) % mod;
}
for(int i = R - L; i; --i)
for(int j = (i << 1); j <= R - L; j += i) dp[i] = (dp[i] - dp[j] + mod) % mod;
if(L == 1) dp[1] = (dp[1] + 1) % mod;
write(dp[1]), enter;
return 0;
}