【题解】P3172 [CQOI2015] 选数(倍数莫反做法)
题面:从 选
个数(可能重复),一共有
种取法。问这些取法中满足
个数最大公约数为
的有多少种。
第一眼就知道要把 ,
。
然后问题就变成求满足 个数最大公约数为
有多少种。
直接来解,复习下莫反公式 :
倍数形式:,不会点这里。
考虑构造 ,结合题目则有两个函数的定义:
:从
中选
个数的最大公约数是
或者
的倍数的情况数。
:从
中选
个数的最大公约数是
的情况数。
那么就有:。
我们只需要最大公约数为 的情况,所以:
(其实这里的 严格意义上是无限大的,但本题最大值只能取到
)
现在我们来看看怎么求 :
回顾定义,发现应该是:
很好,差一点就能分块加速了。。
其实我们可以让 ,这样直接
(当 的情况也符合,大家可以自己试下)
那最后就变成了:
代码:
#include<bits/stdc++.h>
using namespace std;
template<typename T> void qread(T &x){x=0; int f=1; char c=getchar();for(; !isdigit(c); c=getchar()) if(c=='-') f=-1;for(; isdigit(c); c=getchar()) x=x*10+(c-'0');x*=f;
}
typedef long long LL;
const int N=5e6+10;
const LL P=1e9+7;
int pr, prime[N];
LL mu[N];
bool v[N];
void init(){pr=0; memset(v, 0, sizeof(v));mu[1]=1; mu[0]=0;for(int i=2; i<=N-10; i++){if(!v[i]){pr++; prime[pr]=i;mu[i]=-1;} for(int j=1; (j<=pr) && (i*prime[j]<=N-10); j++){int p=prime[j];v[i*p]=1;if(i%p==0){mu[i*p]=0;break;}else mu[i*p]=-mu[i]; }}for(int i=1; i<=N-10; i++) mu[i]+=mu[i-1];
}
LL q_pow(LL a, LL b){LL res=1;while(b){if(b&1) res=res*a%P;a=a*a%P; b/=2;}return res;
}
LL n, K, L, H;
map<LL, LL> hsm;
LL calc(LL x){if(x<=N-10) return mu[x];if(hsm.find(x)!=hsm.end()) return hsm[x];LL res=1;for(int i=2, j; i<=x; i=j+1){j=x/(x/i);res=(res-calc(x/i)*(j-i+1)%P+P)%P;}return hsm[x]=res;
}
LL solve(){LL res=0; L--;for(int i=1, j; i<=H; i=j+1){if(!(L/i)) j=H/(H/i); //到了一定位置大家的 L/i都变成 0,没有参考价值了 else j=min(L/(L/i), H/(H/i));res=(res+q_pow(H/i-L/i, n)*((calc(j)-calc(i-1))%P+P)%P)%P;}return res;
}
int main(){init();qread(n); qread(K); qread(L); qread(H);H=H/K; L=((L%K==0)? L=L/K: L=L/K+1);printf("%lld\n", solve());return 0;
}