[CQOI2015]选数
Posted mrclr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[CQOI2015]选数相关的知识,希望对你有一定的参考价值。
嘟嘟嘟
首先问题可以转化一下,变成在([ lceil frac{L}{k}
ceil, lfloor frac{R}{k}
floor])中选取(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;
}
以上是关于[CQOI2015]选数的主要内容,如果未能解决你的问题,请参考以下文章