【JZOJ 5054】统计

来源:互联网 发布:ubuntu c 开发环境 编辑:程序博客网 时间:2024/06/05 00:21

Description

给定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)后输出

Solution

gn表示只满足条件1,2,3的序列个数,
那么:

Ans=i=1nμ(i)gni

分块以后直接用杜教筛做μ即可;

发现:

gn=Ckn+k1

直接暴力算g发现复杂度会炸,又发现其实ni在大部分时间不会很大,所以就先预处理一些小的阶层,打的直接算,反正不多。

复杂度:O(n23?)

Code

#include <iostream>#include <cstdio>#include <cstdlib>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fod(i,a,b) for(int i=a;i>=b;i--)using namespace std;typedef long long LL;const int N=200500,mo=1e9+7,M=2069317;int m,n;int mu[N+1],pr[N/5];bool prz[N+1];LL Hx[M][2],ans;LL jc[N+1],jcn[N+1];LL ksm(LL q,LL w){    LL ans=1;    while(w)    {        if(w&1)ans=ans*q%mo;        q=q*q%mo;        w>>=1;    }    return ans;}int HX(int q){    int i=q%M;    while(Hx[i][0]&&Hx[i][0]!=q)i=(i+1)%M;    return i;}LL Gmu(int n){    if(n<=N)return mu[n];    int t=HX(n);    if(Hx[t][0])return Hx[t][1];    Hx[t][0]=n;    LL ans=0;    for(int i=2,nx;i<=n;i=nx+1)    {        nx=n/(n/i);        ans=(ans+(LL)(nx-i+1)*Gmu(n/i))%mo;    }    return Hx[t][1]=1-ans;}LL JC(int l,int r){    if(r<=N)return jc[r]*jcn[l-1]%mo;    LL ans=1;    fo(i,l,r)ans=ans*(LL)i%mo;    return ans;}int main(){    int q,w;    mu[1]=1;    fo(i,2,N)    {        if(!prz[i])pr[++pr[0]]=i,mu[i]=-1;        fo(j,1,pr[0])        {            int t=pr[j]*i;            if(t>N)break;            prz[t]=1;            if(i%pr[j]==0)break;            mu[t]=-mu[i];        }    }    fo(i,2,N)mu[i]+=mu[i-1];    jc[1]=1;    fo(i,2,N)jc[i]=jc[i-1]*(LL)i%mo;    jcn[N]=ksm(jc[N],mo-2);    fod(i,N-1,0)jcn[i]=jcn[i+1]*(i+1)%mo;    int _;    scanf("%d",&_);    while(_--)    {        scanf("%d%d",&n,&m);        ans=0;        for(int i=1,nx;i<=n;i=nx+1)        {            nx=n/(n/i);            ans=(ans+(Gmu(nx)-Gmu(i-1))%mo*JC(n/i,n/i+m-1))%mo;        }        printf("%lld\n",(ans+mo)%mo*ksm(JC(1,m),mo-2)%mo);    }    return 0;}
0 0
原创粉丝点击