[BZOJ 3930] [CQOI 2015]选数(莫比乌斯反演+杜教筛)
[BZOJ 3930] [CQOI 2015]选数(莫比乌斯反演+杜教筛)
题面
我们知道,从区间[L,R](L和R为整数)中选取N个整数,总共有(R−L+1)N种方案。求最大公约数刚好为K的选取方案有多少个。由于方案数较大,你只需要输出其除以1000000007的余数即可。
N,K,L,H≤109,H−L≤105
分析
\because \gcd(ka,kb)=k\gcd(a,b),我们先把L,R除以K,然后问题就变成了求gcd=1的方案数
设f(x)表示区间[l,r]里选n个数,gcd为x的方案数
设F(x)表示区间[l,r]里选n个数,gcd被x整除的方案数
\because x|\gcd(i,j),\therefore x|i,x|j
[l,r]里被x整除的数有(\lfloor \frac{r}{x} \rfloor-\lfloor \frac{l-1}{x} \rfloor)个
因此F(x)=(\lfloor \frac{r}{x} \rfloor-\lfloor \frac{l-1}{x} \rfloor)^n
F,f显然满足莫比乌斯反演的第二种形式,F(x)=\sum_{x|d} f(d)
f(x)=\sum_{x|d} F(d) \mu(\frac{d}{x})
我们要求的是
f(1)=\sum_{1|d} F(d) \mu(d)=\sum_{d=1}^r \mu(d) (\lfloor \frac{r}{d} \rfloor-\lfloor \frac{l-1}{d} \rfloor)^n
后面的部分可以数论分块然后快速幂求解,但由于r \leq 10^9,不能直接线性筛\mu的前缀和,需要用杜教筛。
杜教筛:
套路公式:
我们要求f的前缀和,构造两个函数g,h满足h=f*g, F,G,H为它们的前缀和
g(1)F(n)=H(n)-\sum_{d=2}^n g(d) F(\frac{n}{d})
如果f=\mu,注意到\mu*I=\varepsilon,那么g(n)=I(n)=1,h(n)=\varepsilon(n),H(n)=\varepsilon(1)=1
代入得F(n)=1-\sum_{d=2}^n F(\frac{n}{d})
代码
#include<iostream> #include<cstdio> #include<cstring> #include<map> #define maxn 2000000 #define mod 1000000007 using namespace std; typedef long long ll; int n,k; ll A,B; int cnt; bool vis[maxn+5]; int prime[maxn+5]; int mu[maxn+5]; ll s_mu[maxn+5]; void sieve(int n){ mu[1]=1; for(int i=2;i<=n;i++){ if(!vis[i]){ prime[++cnt]=i; mu[i]=-1; } for(int j=1;j<=cnt&&i*prime[j]<=n;j++){ vis[i*prime[j]]=1; if(i%prime[j]==0){ mu[i*prime[j]]=0; break; }else mu[i*prime[j]]=-mu[i]; } } for(int i=1;i<=n;i++) s_mu[i]=(s_mu[i-1]+mu[i])%mod; } map<ll,ll>sum_mu; ll dujiao_sieve(ll x){ if(x<=maxn) return s_mu[x]; if(sum_mu.count(x)) return sum_mu[x]; ll ans=1; for(ll l=2,r;l<=x;l=r+1){ r=x/(x/l); ans-=(r-l+1)*dujiao_sieve(x/l)%mod; ans=(ans+mod)%mod; } sum_mu[x]=ans; return ans; } inline ll fast_pow(ll x,ll k){ ll ans=1; while(k){ if(k&1) ans=ans*x%mod; x=x*x%mod; k>>=1; } return ans; } int main(){ sieve(maxn); scanf("%d %d %lld %lld",&n,&k,&A,&B); A=(A-1)/k; B/=k; ll ans=0; for(ll l=1,r;l<=B;l=r+1){ if(A/l) r=min(A/(A/l),B/(B/l)); else r=B/(B/l); // printf("%d %d\n",l,r); ans+=fast_pow(B/l-A/l,n)*(dujiao_sieve(r)-dujiao_sieve(l-1)+mod)%mod; ans%=mod; } printf("%lld\n",ans); }
版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢
【推荐】2025 HarmonyOS 鸿蒙创新赛正式启动,百万大奖等你挑战
【推荐】博客园的心动:当一群程序员决定开源共建一个真诚相亲平台
【推荐】开源 Linux 服务器运维管理面板 1Panel V2 版本正式发布
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 别做抢活的导演:代码中的抽象层次原则
· 从 Redis 客户端超时到 .NET 线程池挑战
· C23和C++26的#embed嵌入资源指南
· 「EF Core」框架是如何识别实体类的属性和主键的
· 独立开发,这条路可行吗?
· 从 Redis 客户端超时到 .NET 线程池挑战:饥饿、窃取与阻塞的全景解析
· 2025年中总结:我想我克服公众演讲的恐惧了,一个社恐分子突破自我的故事
· 阿里巴巴为什么禁止超过3张表join?
· 实践经验:互联网项目起步指南
· 让 AI 帮我部署网站,太方便了!