[51nod1375]再选数

来源:互联网 发布:苹果数据大师破解版 编辑:程序博客网 时间:2024/06/10 12:28

Description

给出n个数,{a},求在其中选k个数使其gcd为1的方案数。
如果k=-1表示任意取,不过至少得取一个。
答案对998244353取模。
n<=10^5,ai<=10^6

Solution

记得某位dalao说过,看到gcd想到反演=w=
但这道题都能用反演?!涨姿势了~
开始一直在想容斥,想不出来,然后去Orz了栋爷的题解(数论蒟蒻不要鄙视我QwQ
然后看到第一句话就震惊了233
话说反演真的很好推呀!
设Fd表示gcd为d的方案数。
Gd表示gcd为d的倍数的方案数。
然后对每个数分解因数,求出有多少个数是d的倍数,那么用组合数就可以算出C了。
对于k=-1的情况预处理2的幂就好了,记得要-1,因为必须至少选一个。
然后就没有了,劲啊

Code

#include <cmath>#include <cstdio>#include <cstring>#include <algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,a,b) for(int i=a;i>=b;i--)using namespace std;typedef long long ll;const int N=1e6+5,mo=998244353;int fact[N],inv[N],mi[N],cnt[N],g[N],mx,n,k,x,ans;int p[N],mu[N];bool bz[N];int c(int m,int n) {    if (n>m) return 0;    return (ll)fact[m]*inv[n]%mo*inv[m-n]%mo;}int main() {    scanf("%d%d",&n,&k);    fo(i,1,n) {        scanf("%d",&x);        mx=max(mx,x);int up=sqrt(x);        fo(j,1,up)             if (!(x%j)) {                cnt[j]++;                if (j*j!=x) cnt[x/j]++;            }    }    fact[0]=inv[0]=mi[0]=inv[n]=1;    fo(i,1,n) fact[i]=(ll)fact[i-1]*i%mo,mi[i]=mi[i-1]*2%mo;    for(int y=mo-2,x=fact[n];y;y/=2,x=(ll)x*x%mo)         if (y&1) inv[n]=(ll)inv[n]*x%mo;    fd(i,n-1,1) inv[i]=(ll)inv[i+1]*(i+1)%mo;    fo(i,2,mx) {        if (!bz[i]) p[++p[0]]=i,mu[i]=-1;        fo(j,1,p[0]) {            int k=i*p[j];if (k>mx) break;            bz[k]=1;if (!(i%p[j])) break;            mu[k]=-mu[i];        }    }    mu[1]=1;    fo(i,1,mx) if (k!=-1) g[i]=c(cnt[i],k);else g[i]=mi[cnt[i]]-1;    fo(i,1,mx) (ans+=mu[i]*g[i])%=mo;    printf("%d\n",(ans+mo)%mo);}
0 0
原创粉丝点击