[CQOI2015][bzoj3930] 选数 [杜教筛+莫比乌斯反演]
题面:
思路:
首先我们把区间缩小到$\left[\lfloor\frac{L-1}{K}\rfloor,\lfloor\frac{R}{K}\rfloor\right]$
这道题的最特殊的点在于,他的gcd不是两个数的而是多个数的,是一坨sigma
但是,我们发现它依然可以反演
令$f\left(i\right)$为区间$\left[l,r\right]$内选出$n$个数,总计$gcd=i$的方法数
令$g\left(i\right)$为区间$\left[l,r\right]$内选出$n$个数,总计$i|gcd$的方法数
那么依旧满足$g(d)=\sum_{d|i}f\left(i\right)$,反演后得到$f(d)=\sum_{d|i}\mu\left(\frac id\right)g\left(i\right)$
因此$f\left(d\right)=\sum_{i=1}^{\frac nd}\left(\lfloor\frac Rd\rfloor-\lfloor\frac Ld\rfloor\right)^n$
答案即为对于缩小过的$L,R$,$f\left(1\right)$的值
因为后半部分的可以用快速幂加数论分块做到$O\left(\sqrt n\right)$
所以前半部分杜教筛$\mu$即可
Code:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<map> 6 #define ll long long 7 using namespace std; 8 inline ll read(){ 9 ll re=0,flag=1;char ch=getchar(); 10 while(ch>'9'||ch<'0'){ 11 if(ch=='-') flag=-1; 12 ch=getchar(); 13 } 14 while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar(); 15 return re*flag; 16 } 17 ll pri[2000010],tot=0,mu[2000010],n,K,L,R;bool vis[2000010]; 18 ll MOD=1e9+7; 19 void init(){ 20 ll i,j,k;mu[1]=1; 21 for(i=2;i<=2000000;i++){ 22 if(!vis[i]){ 23 pri[++tot]=i;mu[i]=-1; 24 } 25 for(j=1;j<=tot;j++){ 26 k=i*pri[j];if(k>2000000) break; 27 vis[k]=1; 28 if(i%pri[j]==0){ 29 mu[k]=0;break; 30 } 31 mu[k]=-mu[i]; 32 } 33 } 34 for(i=2;i<=2000000;i++) mu[i]=mu[i-1]+mu[i]; 35 } 36 ll sum1(ll x){return x*(x+1)/2;} 37 map<ll,ll>m; 38 ll S2(ll x){ 39 if(x<=2000000) return mu[x]; 40 ll re=1,i,j; 41 if(m[x]) return m[x]; 42 for(i=2;i<=x;i=j+1){ 43 j=x/(x/i); 44 re-=((j-i+1)*S2(x/i))%MOD; 45 re=(re+MOD)%MOD; 46 } 47 return m[x]=re; 48 } 49 ll ppow(ll a,ll b){ 50 ll re=1; 51 while(b){ 52 if(b&1) re=re*a%MOD; 53 a=a*a%MOD;b>>=1; 54 } 55 return re%MOD; 56 } 57 int main(){ 58 init(); 59 n=read();K=read();L=read();R=read(); 60 L=(L-1)/K;R=R/K; 61 ll i,j;ll ans=0; 62 for(i=1;i<=R;i=j+1){ 63 j=R/(R/i); 64 if(i<=L) j=min(j,L/(L/i)); 65 ans=(ans+(S2(j)-S2(i-1)+MOD)%MOD*ppow(R/i-L/i,n)%MOD)%MOD; 66 } 67 printf("%lld\n",ans); 68 }