题目
3930:[CQOI2015]选数
TimeLimit:10Sec
MemoryLimit:512MB
Description
我们知道,从区间
[L,H]
(
L
和
Input
输入一行,包含
4
个空格分开的正整数,依次为
Output
输出一个整数,为所求方案数。
SampleInput
2 2 2 4
SampleOutput
3
HINT
样例解释
所有可能的选择方案:(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2), (4, 3), (4, 4)
其中最大公约数等于2的只有3组:(2, 2), (2, 4), (4, 2)
对于100%的数据,1≤N,K≤109,1≤L≤H≤109,H−L≤105
题解
看到
gcd
,第一反应这应该是莫反。
然而,这可以用数学方法。
没想到吧,我也没想到。
设
l=⌈LK⌉
,
h=⌊HK⌋
。
首先,我们可以把题目转化为从区间
[l,h]
中选取
N
个整数,求
我们假设
设
f(i)
表示
i|gcd
的方案数,
g(i)
表示
i=gcd
的方案数。
设
ll=⌈li⌉
,
hh=⌊hi⌋
。
则
f(i)=(hh−ll+1)n−(hh−ll+1)
(因为当且仅当是
i
的倍数时可以被选)
那么从
H−L
逆推到
1
即可。
时间复杂度
O((H−L)logN)
。
总结
这道题其实可以莫反,也可以数学方法。但是我的第一反应仍是数学方法。这种题目如果不会莫反,就不要去惹它。想一想骗分的排列组合,说不定就过了。
不想新建分类,就当做是莫反。
标程
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MOD=1000000007;
const int N=100005;
int n, k, l, h, ans;
int ll, hh, f[N];
int power(int a, int b)
{
int c=1;
while (b)
{
if (b&1) c=1LL*c*a%MOD;
b>>=1; a=1LL*a*a%MOD;
}
return c;
}
void work()
{
scanf("%d%d%d%d", &n, &k, &l, &h);
l=(l-1)/k+1; h/=k;
for (int i=1; i<=h-l; i++)
{
ll=(l-1)/i+1; hh=h/i;
f[i]=(power(hh-ll+1, n)-(hh-ll+1)+MOD)%MOD;
}
for (int i=h-l; i; i--)
for (int j=(i<<1); j<=h-l; j+=i)
f[i]=(f[i]-f[j]+MOD)%MOD;
if (l==1) f[1]=(f[1]+1)%MOD;
printf("%d\n", f[1]);
}
int main()
{
work();
return 0;
}