[51nod1375]再选数

来源:互联网 发布:辐射3 知乎 编辑:程序博客网 时间:2024/06/06 11:38

题目大意

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