【递推】BZOJ 3930: [CQOI2015]选数
Description
我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案。小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一步研究。然而他很快发现工作量太大了,于是向你寻求帮助。你的任务很简单,小z会告诉你一个整数K,你需要回答他最大公约数刚好为K的选取方案有多少个。由于方案数较大,你只需要输出其除以1000000007的余数即可。
Input
输入一行,包含4个空格分开的正整数,依次为N,K,L和H。
Output
输出一个整数,为所求方案数。
这道题有两种做法- -
1.递推
我们先设在一段区间[l,r]间选择n个元素,且它们的gcd为k*i的选择方案是f[i]。
显然,[l,r]内能被k*i整除的数有(R-L+1)^n个(R=r/(i*k),L=l/(i*k))。但是,有一些选择是这种(L,L,L,L,L,L,...L),一共有(R-L+1)种,同时还有最大公约数是k*i的倍数的,我们也要减去。
得到f[i]=(R-L+1)^n-(R-L+1)-f[k*i*a](a>=2 && k*i*a<=L-R+1)。
输出f[1]即可。
但是还有特殊情况。就是k在[l,r]间,所以这时f[1]++即可。
2.mobius反演
公式还是蛮容易的。。
mobius公式推导:http://lzy-foenix.gitcafe.io/2015/04/09/BZOJ-3930-CQOI2015-%E9%80%89%E6%95%B0/
关于阀值与μ的推导:http://www.cnblogs.com/Asm-Definer/p/4434601.html
PoPoQQQ的两者结合:http://blog.csdn.net/popoqqq/article/details/44917831(画质感人- -)
My Code
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 6 #define mod 1000000007 7 8 #define maxn 100000 9 10 using namespace std; 11 12 long long f[maxn+1]; 13 14 long long qvod(long long x,long long k) 15 { 16 long long ans=1; 17 while(k!=0) 18 { 19 if(k&1)ans=ans*x%mod; 20 x=x*x%mod; 21 k>>=1; 22 } 23 return ans; 24 } 25 26 int main() 27 { 28 int a,b,k,n; 29 scanf("%d%d%d%d",&n,&k,&a,&b); 30 int l=a/k,r=b/k; 31 if(a%k)l++; 32 for(int i=maxn;i>=1;i--) 33 { 34 int L=l/i,R=r/i; 35 if(l%i)L++; 36 if(l<=r) 37 { 38 f[i]=qvod(R-L+1,n); 39 f[i]=(f[i]-(R-L+1)+mod)%mod; 40 for(int j=i*2;j<=maxn;j+=i)f[i]=(f[i]-f[j]+mod)%mod; 41 } 42 } 43 if(l==1)f[1]++; 44 printf("%lld",(f[1]+mod)%mod); 45 return 0; 46 }
忽视奇怪的快速幂