[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 }

 

posted @ 2018-03-08 19:21  dedicatus545  阅读(209)  评论(0编辑  收藏  举报