题目描述
从前有n个正整数,我们令它为a[1]到a[n]。现在要从选出恰好k个数(如果k=-1则为选任意个数,但是至少选一个),要求这些数的最大公约数是1,问有多少种方案?
答案可能很大,模998,244,353输出。
搞一发
可以设f[x]表示选出恰好k个数gcd为x的方案数。
设g[x]表示选出恰好k个数gcd为x的倍数。
于是就可以莫比乌斯反演一发。
求f[1]即可。
#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=100000+10,maxd=1000000+10,mo=998244353;
int mu[maxd],pri[maxn],f[maxd],g[maxd],h[maxd],cnt[maxd],fac[maxn],inv[maxn],two[maxn];
int b[100],c[100];
bool bz[maxd];
int i,j,k,l,t,n,m,top,tot,ans;
int quicksortmi(int x,int y){
if (!y) return 1;
int t=quicksortmi(x,y/2);
t=(ll)t*t%mo;
if (y%2) t=(ll)t*x%mo;
return t;
}
void dfs(int x,int y){
if (x==tot+1){
cnt[y]++;
return;
}
int i,j=1;
fo(i,0,c[x]){
dfs(x+1,y*j);
if (i<c[x]) j*=b[x];
}
}
int C(int n,int m){
if (n<m) return 0;
return (ll)fac[n]*inv[m]%mo*inv[n-m]%mo;
}
int main(){
scanf("%d%d",&n,&k);
fac[0]=1;
fo(i,1,n) fac[i]=(ll)fac[i-1]*i%mo;
inv[n]=quicksortmi(fac[n],mo-2);
fd(i,n-1,0) inv[i]=(ll)inv[i+1]*(i+1)%mo;
two[0]=1;
fo(i,1,n) two[i]=(ll)two[i-1]*2%mo;
mu[1]=1;
fo(i,2,maxd-10){
if (!bz[i]){
pri[++top]=i;
f[i]=g[i]=i;
h[i]=1;
mu[i]=-1;
}
fo(j,1,top){
if ((ll)i*pri[j]>maxd-10) break;
bz[i*pri[j]]=1;
if (i%pri[j]==0){
f[i*pri[j]]=pri[j];
g[i*pri[j]]=g[i]*pri[j];
h[i*pri[j]]=h[i]+1;
mu[i*pri[j]]=0;
break;
}
f[i*pri[j]]=g[i*pri[j]]=pri[j];
h[i*pri[j]]=1;
mu[i*pri[j]]=-mu[i];
}
}
fo(i,1,n){
scanf("%d",&t);
tot=0;
while (t>1){
b[++tot]=f[t];
c[tot]=h[t];
t/=g[t];
}
dfs(1,1);
}
fo(i,1,maxd-10)
if (k==-1) g[i]=two[cnt[i]]-1;else g[i]=C(cnt[i],k);
fo(i,1,maxd-10) (ans+=g[i]*mu[i])%=mo;
(ans+=mo)%=mo;
printf("%d\n",ans);
}