题目大意
有n个正整数,你可以从中选出k个数(k是给定的,如果k=-1则要选择至少一个),使它们的gcd=1。求方案数模998244353的值。
n≤100000 每个数≤1000000
分析
数的范围不是很大,可以考虑从中入手。
设f(d)为选择的数gcd为d的答案,g(d)表示gcd是d的倍数的答案。显然有g(d)=
∑f(kd)
看到这里想到了什么?!
f(d)=∑i=1106dg(id)∗μi
答案就是f(1),剩下的是预处理g数组。
考虑k=-1的情况,假设有s(d)个数是d的倍数,那么 g(d)=2s(d)−1
k≠-1时, g(d)=Cks(d)
对读入的数根号复杂度枚举它的约数,然后统计答案是线性的。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M=1000000,mo=998244353;
typedef long long LL;
int n,k,tot,pr[M+5],cnt[M+5],ans,Fac[M],Inv[M],Fac_Inv[M],P[M+5],mu[M+5];
bool bz[M+5];
char c;
int read()
{
for (c=getchar();c<'0' || c>'9';c=getchar());
int x=c-48;
for (c=getchar();c>='0' && c<='9';c=getchar()) x=x*10+c-48;
return x;
}
void init()
{
mu[1]=1;
for (int i=2;i<=M;i++)
{
if (!bz[i])
{
pr[tot++]=i; mu[i]=-1;
}
for (int j=0;j<tot;j++)
{
int I=i*pr[j];
if (I>M) break;
bz[I]=1;
if (i%pr[j]==0)
{
mu[I]=0; break;
}
mu[I]=-mu[i];
}
}
}
int C(int n,int m)
{
return (LL)Fac[n]*Fac_Inv[m]%mo*Fac_Inv[n-m]%mo;
}
int main()
{
init();
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++)
{
int x=read(),j;
for (j=1;j*j<x;j++) if (x%j==0)
{
cnt[j]++; cnt[x/j]++;
}
if (j*j==x) cnt[j]++;
}
if (k==-1)
{
P[0]=1;
for (int i=1;i<=n;i++) P[i]=P[i-1]*2%mo;
for (int i=1;i<=M;i++)
{
ans=(ans+mu[i]*(P[cnt[i]]-1))%mo;
}
ans=(ans+mo)%mo;
printf("%d\n",ans);
return 0;
}
Fac[0]=Fac[1]=Fac_Inv[0]=Fac_Inv[1]=Inv[1]=1;
for (int i=2;i<=n;i++)
{
Fac[i]=(LL)Fac[i-1]*i%mo;
Inv[i]=(LL)Inv[mo%i]*(mo-mo/i)%mo;
Fac_Inv[i]=(LL)Fac_Inv[i-1]*Inv[i]%mo;
}
for (int i=1;i<=M;i++) if (cnt[i]>=k)
{
ans=(ans+mu[i]*C(cnt[i],k))%mo;
}
ans=(ans+mo)%mo;
printf("%d\n",ans);
return 0;
}