【jzoj5054】【统计】【杜教筛】

题目大意

给定n,k,求满足一下条件的整数数组a[]的数量:

1.a[]中共有k个元素;

2. a[i] ∈ [1,n];

3. ∀i∈[1,k),a[i]≤a[i+1];

4、gcd(a1,a2…ak)=1

答案可能很大,请mod(109+7)后输出

解题思路

观察可知 ans=ni=1f(n/d)mu[d],f(x)=ckx+k1

我们需要快速求出mu的前缀和 s(x)=xi=1mu[i]=xi=1d|imu[d]xi=1d|i(d!=i)mu[d]=1xi=2x/id=1mu[d]=1xi=2s(x/i)

这样我们只需要预处理出一部分的s,剩下的需要再算,用hash记忆化即可。

code

#include
#include
#include
#include
#include
#define LD double
#define LL long long
#define ULL unsigned long long
#define min(a,b) ((a
#define max(a,b) ((a>b)?a:b)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define fr(i,j) for(int i=begin[j];i;i=next[i])
using namespace std;
int const lim=1e6,size=1e6,inf=1e9,mo=1e9+7;
int t,n,k,ni,mu[lim+9],tag[lim+9],ss[lim+9],fact[lim+9],nfact[lim+9],h[size+9],
    f[size+9];
int c(int x,int y){
    if(x<=lim)return 1ll*fact[x]*nfact[x-y]%mo*ni%mo;
    LL tmp=1;
    fd(i,x,x-y+1)tmp=tmp*i%mo;
    return tmp*ni%mo;
}
int Pow(LL x,int y){
    LL tmp=1;
    while(y){
        if(y&1)tmp=tmp*x%mo;
        x=x*x%mo;
        y>>=1;
    }
    return tmp;
}
int hash(int x){
    int p=x%size;
    while(h[p]&&(h[p]!=x))p=(p==size-1)?0:p+1;
    return p;
}
int s(int x){
    if(x<=lim)return mu[x];
    int t=hash(x);
    if(h[t])return f[t];
    LL tmp=1;
    for(int i=2,j;i<=x;i=j+1){
        j=x/(x/i);
        tmp-=1ll*s(x/i)*(j-i+1);
    }
    h[t]=x;f[t]=tmp=(tmp%mo+mo)%mo;
    return tmp; 
}
int main(){
    //freopen("count.in","r",stdin);
    //freopen("count.out","w",stdout);
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d",&t);
    fact[0]=nfact[0]=fact[1]=nfact[1]=1;
    fo(i,2,lim){
        fact[i]=1ll*fact[i-1]*i%mo;
        nfact[i]=Pow(fact[i],mo-2);
        if(!tag[i])ss[++ss[0]]=i,mu[i]=-1;
        fo(j,1,ss[0]){
            if(i*ss[j]>lim)break;
            tag[i*ss[j]]=1;
            mu[i*ss[j]]=-mu[i];
            if(i%ss[j]==0){
                mu[i*ss[j]]=0;
                break;
            }
        }
    }
    mu[1]=1;fo(i,2,lim)mu[i]+=mu[i-1];
    fo(cas,1,t){
        scanf("%d%d",&n,&k);int ans=0;
        ni=1;fo(i,2,k)ni=1ll*ni*Pow(i,mo-2)%mo;
        for(int i=1,j;i<=n;i=j+1){
            j=n/(n/i);
            ans=(ans+1ll*c(n/i+k-1,k)*(s(j)-s(i-1)))%mo;
        }
        printf("%d\n",(ans+mo)%mo);
    }
    return 0;
}

你可能感兴趣的:(jzoj,数论)