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;
}