[CQOI2015]选数
题目描述
我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案。小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一步研究。然而他很快发现工作量太大了,于是向你寻求帮助。你的任务很简单,小z会告诉你一个整数K,你需要回答他最大公约数刚好为K的选取方案有多少个。由于方案数较大,你只需要输出其除以1000000007的余数即可。
输入输出格式
输入格式:
输入一行,包含4个空格分开的正整数,依次为N,K,L和H。
输出格式:
输出一个整数,为所求方案数。
输入输出样例
2 2 2 4
3
说明
样例解释
所有可能的选择方案:(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<=10^9,1<=L<=H<=10^9,H-L<=10^5
此题有巧解
首先将L/k,R/k,gcd=k/k=1
令f(x)为gcd=x的方案数
F(i)为x能被i整除的方案数
F(i)=∑df(d) (i|d)
反演得f(d)=∑iμ(i/d)F(i) (d|i)
f(1)=∑μ(i)F(i) (1<=i<=H)
=∑μ(i)([r/i]-[(l-1)/i])^n
可知当i>H-L时,[r/i]-[(l-1)/i]等于1或0,所以可以去掉指数
f(1)=∑μ(i)([r/i]-[(l-1)/i])+∑μ(j)([r/j]-[(l-1)/j])^n (H-L+1<=i<=H,1<=j<=H-L)
因为H-L很小,直接枚举加快速幂搞定,对于左边
∑μ(i)([r/i]-[(l-1)/i]) (H-L+1<=i<=H)
=∑μ(i)([r/i]-[(l-1)/i])-∑μ(j)([r/j]-[(l-1)/j]) (1<=i<=H,1<=j<=H-L)
右边快速幂,而∑μ(i)([r/i]-[(l-1)/i])可以等价于取一个数,使gcd=1的方案数,显然只有当L=1时+1
复杂度为O((H-L)*log(n))
似乎此题还可以用递推??
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 int Mod=1000000007; 7 typedef long long lol; 8 bool vis[100001]; 9 lol mu[100001],r,l,n,k,ans; 10 int tot,prime[100001]; 11 lol qpow(lol x,lol y) 12 { 13 lol res=1; 14 while (y) 15 { 16 if (y%2==1) 17 { 18 res=(res*x)%Mod; 19 } 20 x=(x*x)%Mod; 21 y=y/2; 22 } 23 return res; 24 } 25 void mobius() 26 {lol i,j; 27 mu[1]=1; 28 for (i=2;i<=r-l;i++) 29 { 30 if (vis[i]==0) 31 { 32 tot++; 33 prime[tot]=i; 34 mu[i]=-1; 35 } 36 for (j=1;j<=tot,i*prime[j]<=r-l;j++) 37 { 38 vis[i*prime[j]]=1; 39 if (i%prime[j]==0) 40 { 41 mu[i*prime[j]]=0; 42 break; 43 } 44 mu[i*prime[j]]=-mu[i]; 45 } 46 } 47 } 48 int main() 49 {int i; 50 cin>>n>>k>>l>>r; 51 l=(l+k-1)/k;r=r/k; 52 mobius(); 53 for (i=1;i<=r-l;i++) 54 { 55 ans=(ans+mu[i]*qpow(r/i-(l-1)/i,n)%Mod)%Mod; 56 } 57 if (l==1) ans++; 58 for (i=1;i<=r-l;i++) 59 { 60 ans=(ans-mu[i]*(r/i-(l-1)/i))%Mod; 61 } 62 cout<<(ans+Mod)%Mod; 63 }