[CQOI2015]选数(莫比乌斯反演,杜教筛)
Description
题目描述
我们知道,从区间 [L,H](L 和 H 为整数)中选取 N 个整数,总共有 (H-L+1)^N 种方案。
小 z 很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的 N 个整数都求一次最大公约数,以便进一步研究。
然而他很快发现工作量太大了,于是向你寻求帮助。你的任务很简单,小 z 会告诉你一个整数 K,
你需要回答他最大公约数刚好为 K 的选取方案有多少个。
由于方案数较大,你只需要输出其除以 10^9+7 的余数即可。
输入格式
输入一行,包含四个空格分开的正整数,依次为 N,K,L,H。
输出格式
输出一个整数,为所求方案数。
数据范围
1 ≤ N,K ≤ 10^9,1 ≤ L ≤ H ≤ 10^9,H−L ≤ 10^5。
Soluiton
- 先将 L、H 分别化为比 L、H 小的第一个 K 的倍数 / K
- 问题变成从 ( L , H ] 中选 N 个可重复的数,使它们的 gcd 为 1 的方案数
- 设 f(i)为从 ( L , H ] 中选 N 个可重复的数,使它们的 gcd 为 i 的方案数,
- 发现 $F(i)=\sum\limits_{ i | d}^{} f(d)=(\lfloor H \rfloor - \lfloor L \rfloor)^N$,好求!
反演一下
$$f(i)=\sum\limits_{i|d,d<=H}^{}\mu(d)*F(d/i)$$
- 然后整除分块,注意同时考虑 L,H
- d可能很大,线性筛死了,杜教筛还活着
Code
#include <cstdio> #include <cstdlib> #include <map> #include <algorithm> #define ll long long using namespace std; const ll P=1e9+7; const int N=6e6,M=6e6+10; int L,H,k,n,prime[M],tot,vis[M]; ll an,u[M]; map <int,ll> w; ll qpow(int x,int y) { ll ans=1,z=x; while(y) { if(y&1) ans=ans*z%P; z=z*z%P,y>>=1; } return ans; } ll F(int x) { return qpow((H/x-L/x),n); } ll get(int x) { if(x<=N) return u[x]; if(w[x]) return w[x]; int ans=1; for(int l=2,r;l<=x;l=r+1) { r=x/(x/l); ans-=(r-l+1)*get(x/l),ans%=P; } return w[x]=ans; } void init() { u[1]=1; for(int i=2;i<=N;i++) { if(!vis[i]) prime[++tot]=i,u[i]=-1; for(int j=1;j<=tot && prime[j]<=N/i;j++) { vis[prime[j]*i]=1; if(i%prime[j]==0) break; u[prime[j]*i]=-u[i]; } u[i]+=u[i-1]; } } int main() { init(); scanf("%d%d%d%d",&n,&k,&L,&H); L=(L-1)/k,H=H/k; for(int l=1,r;l<=H;l=r+1) { if(!(L/l)) r=H/(H/l); else r=min(H/(H/l),L/(L/l)); an=(an+(get(r)-get(l-1))%P*F(l)%P)%P; } printf("%lld\n",(an+P)%P); return 0; }

浙公网安备 33010602011771号