BZOJ 4305 数列的GCD

题目链接:Click here

Solution:

\(f[i]\)表示当\(d=i\)时的答案,\(c[i]\)表示\(a\)序列中有多少个\(i\)的倍数

首先我们要使恰好\(k\)个数互不相同,则表示其他\(n-k\)个数恰好相同,那么有\({c[i]\choose n-k}\)种方案

考虑剩下的\(c[i]-n+k\)个位\(i\)的倍数的位置,\([1,m]\)\(i\)的倍数有\(\lfloor {m \over i} \rfloor\)个,但不能相同,所以要减去自己,方案数即\((\lfloor {m \over i} \rfloor-1)^{c[i]-n+k}\),剩下的位置同理,但不用减去自己,即$\lfloor {m\over i} \rfloor ^{n-c[i]} $

到这里我们发现我们不仅统计了\(i\)自己,还把\(i\)的倍数也算进去了,所以要减掉,则

\[f[i]={c[i] \choose n-k}\times (\lfloor {m \over i} \rfloor -1)^{c[i]-n+k} \times \lfloor {m\over i} \rfloor ^{n-c[i]} - \sum_{j=2}^{\lfloor {m \over i}\rfloor} f[i\times j] \]

于是我们倒着算即可,时间复杂度\(O(n \log n)\)

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
const int N=3e5+11;
int n,m,k,a[N],f[N],c[N],v[N];
int fac[N]={1},ifac[N];
int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
int qpow(int x,int y){
    int re=1;
    while(y>0){
        if(y&1) re=re*x%mod;
        y>>=1;x=x*x%mod;
    }return re;
}
int C(int x,int y){
    if(x<y) return 0;
    return fac[x]*ifac[y]%mod*ifac[x-y]%mod;
}
signed main(){
    n=read(),m=read(),k=read();
    for(int i=1;i<=n;i++) ++v[a[i]=read()];
    for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
    ifac[n]=qpow(fac[n],mod-2);ifac[0]=1;
    for(int i=n-1;i;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
    for(int i=1;i<=m;i++)
        for(int j=1;j*i<=m;j++)
            c[i]+=v[i*j];
    for(int i=m;i;i--){
        f[i]=C(c[i],n-k)*qpow(m/i-1,c[i]-n+k)%mod;
        f[i]=f[i]*qpow(m/i,n-c[i])%mod;
        for(int j=2;j<=m/i;j++) f[i]=(f[i]+mod-f[i*j])%mod;
    }
    for(int i=1;i<=m;i++) printf("%lld ",f[i]);
    return 0;
}

posted @ 2020-01-29 23:48  DQY_dqy  阅读(171)  评论(0编辑  收藏  举报