[BZOJ3930][CQOI2015]选数

BZOJ
Luogu
原题因为\(H-L\le10^5\)的限制,其实可以不用杜教筛。不过去掉这个限制本题还是可做的。以下题解忽视这个条件并使用了杜教筛。

sol

首先\(L=\lfloor\frac {L-1}{K}\rfloor\)\(R=\lfloor\frac RK\rfloor\),转化为在这个区间里选出n个数使它们的\(\gcd\)等于1.这个显然可以反演得到

\[ans=\sum_{i=1}^{R}\mu(i)(\lfloor\frac Hi\rfloor-\lfloor\frac Li\rfloor)^n \]

后面一坨分块,然后需要快速求出\(\mu(i)\)的前缀和。
杜教筛即可。

code

#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
const int mod = 1000000007;
const int N = 5000000;
int gi()
{
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
int fastpow(int a,int b)
{
    int res=1;
    while (b) {if (b&1) res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
    return res;
}
int n,k,L,R,maxN,pri[N+5],tot,zhi[N+5],mu[N+5];
map<int,int>M;
void Mobius()
{
    zhi[1]=mu[1]=1;
    for (int i=2;i<=N;i++)
    {
        if (!zhi[i]) pri[++tot]=i,mu[i]=-1;
        for (int j=1;j<=tot&&i*pri[j]<=N;j++)
        {
            zhi[i*pri[j]]=1;
            if (i%pri[j]) mu[i*pri[j]]=-mu[i];
            else break;
        }
    }
    for (int i=1;i<=N;i++) mu[i]+=mu[i-1];
}
int Mu(int x)
{
    if (x<=maxN) return mu[x];
    if (M[x]) return M[x];
    int i=2,j,res=0;
    while (i<=x)
    {
        j=x/(x/i);
        res=(res+1ll*(j-i+1)*Mu(x/i)%mod)%mod;
        i=j+1;
    }
    return M[x]=(1-res+mod)%mod;
}
int main()
{
    n=gi();k=gi();L=(gi()-1)/k;R=gi()/k;
    maxN=min(N,R);
    Mobius();
    int i=1,j,ans=0;
    while (i<=R)
    {
        j=R/(R/i);if (L/i) j=min(j,L/(L/i));
        ans=(ans+1ll*(Mu(j)-Mu(i-1)+mod)%mod*fastpow(R/i-L/i,n)%mod)%mod;
        i=j+1;
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2018-01-17 19:51  租酥雨  阅读(201)  评论(1编辑  收藏  举报