题意
从前有n个正整数,我们令它为a[1]到a[n]。现在要从选出恰好k个数(如果k=-1则为选任意个数,但是至少选一个),要求这些数的最大公约数是1,问有多少种方案?
答案可能很大,模998,244,353输出。
1≤n≤100,000;1≤k≤n或k=-1。所有的1≤a[i]≤1,000,000并且是随机生成的。
分析
考虑容斥,用全部方案减去gcd大于1的方案。可以枚举gcd有哪些素因子,设这些素因子的乘积为d,那么容斥系数显然就是 μ(d) ,然后统计一下d的倍数的数量,用组合数算一算即可。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=1000005;
const int MOD=998244353;
int n,m,t[N],prime[N],tot,mu[N],jc[N],ny[N],po[N],k;
bool not_prime[N];
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void get_prime(int n)
{
mu[1]=1;
for (int i=2;i<=n;i++)
{
if (!not_prime[i]) prime[++tot]=i,mu[i]=-1;
for (int j=1;j<=tot&&i*prime[j]<=n;j++)
{
not_prime[i*prime[j]]=1;
if (i%prime[j]==0) break;
mu[i*prime[j]]=-mu[i];
}
}
}
int get_c(int n,int m)
{
if (n<m) return 0;
return (LL)jc[n]*ny[m]%MOD*ny[n-m]%MOD;
}
int main()
{
n=read();k=read();int mx=0;
for (int i=1,x;i<=n;i++) x=read(),mx=max(mx,x),t[x]++;
get_prime(mx);
jc[0]=jc[1]=ny[0]=ny[1]=po[0]=1;po[1]=2;
for (int i=2;i<=n;i++) jc[i]=(LL)jc[i-1]*i%MOD,ny[i]=(LL)(MOD-MOD/i)*ny[MOD%i]%MOD;
for (int i=2;i<=n;i++) ny[i]=(LL)ny[i-1]*ny[i]%MOD,po[i]=po[i-1]*2,po[i]-=po[i]>=MOD?MOD:0;
int ans=k==-1?po[n]-1:get_c(n,k);
for (int d=2;d<=mx;d++)
{
if (!mu[d]) continue;
int s=0;
for (int i=d;i<=mx;i+=d) s+=t[i];
if (k>0) ans+=mu[d]>0?get_c(s,k):MOD-get_c(s,k);
else ans+=mu[d]>0?po[s]-1:MOD-po[s]+1;
ans-=ans>=MOD?MOD:0;
}
printf("%d",ans);
return 0;
}