bzoj 3930: [CQOI2015]选数

来源:互联网 发布:淘宝dsr公式 编辑:程序博客网 时间:2024/05/21 16:56

题意:

在[L,H]中选n个可重复,有序的数,使这些数的gcd=k。

题解:

1A了很爽。
莫比乌斯反演+杜教筛。
先转化题意,设lk=l1k+1   rk=rk
相当于在[lk,rk]中选n个互质的数。
ans=a1lk rka2lk rka1lk rk[gcd(a1,a2,an)=1]
反演一波就得到ans=irkμ(i)(rkilk1i)n
然后杜教筛求μ的前缀和就可以了。杜教筛
code:

#include<cstdio>#include<cstdlib>#include<cstring>#include<map>#include<algorithm>#include<iostream>#define LL long longusing namespace std;LL mu[5000010],prime[5000010];bool v[5000010];map<int,LL> sum;int n,k,l,r,pr;const LL mod=1000000007;void pre(){    memset(v,true,sizeof(v));    pr=0;mu[1]=1;    for(int i=2;i<=5000000;i++)    {        if(v[i]) prime[++pr]=(LL)i,mu[i]=(LL)(-1);        for(int j=1;j<=pr&&(LL)i*prime[j]<=5000000;j++)        {            v[i*prime[j]]=false;            if(i%prime[j]==0){mu[i*prime[j]]=0;break;}            mu[i*prime[j]]=-mu[i];        }    }    mu[0]=0;    for(int i=1;i<=5000000;i++) mu[i]=(mu[i]+mu[i-1])%mod;}LL solve(int n){    if(n<=5000000) return mu[n];    if(sum[n]) return sum[n];    LL ans=1LL;int j;    for(int i=2;i<=n;i=j+1)    {        j=n/(n/i);        ans=(ans-solve(n/i)*(j-i+1))%mod;    }    sum[n]=ans;    return ans;}LL work(LL a,int b){    LL ans=1LL;    while(b)    {        if(b&1) ans=ans*a%mod;        a=a*a%mod;b>>=1;    }    return ans;}int main(){    pre();sum.clear();    scanf("%d %d %d %d",&n,&k,&l,&r);    l=(l-1)/k+1;r/=k;//l=max(l,1);r=max(r,1);    LL ans=0LL;int j;    for(int i=1;i<=r;i=j+1)    {        //ans=(ans+mu[i]*work((r/i-(l-1)/i),n))%mod;        //printf("%d %lld\n",i,ans);        j=r/(r/i);        if((l-1)/i) j=min(j,(l-1)/((l-1)/i));        ans=(ans+(solve(j)-solve(i-1))*work((r/i-(l-1)/i),n))%mod;    }    printf("%lld",(ans+mod)%mod);}