HDU 4675GCD of Sequence(大组合数取模 容斥原理 组合数学)

来源:互联网 发布:简述网络教育的意义 编辑:程序博客网 时间:2024/05/10 12:38

Problem Link

GCD of Sequence
大意,对于序列ai中的找一个序列bis.tgcd(bi)=d,1bi,dm,bi,恰好有k个数与ai不相等,问对于每一个d问这样的序列有多少个,结果MOD 1e9+7;

Analyse

f(d)d|gcdbi的个数,这样

f(d)=d|ngcdbi=n

我们用另一种方式技计数f(d)
sum表示d|ai的个数,这样就有n-sum个数,不整除d,
if n-sum >k,显然f(d)等于0,也就是说这样的序列个数为0,
if nsumk,那就把这n-sum个数变成d的倍数,再从剩下的sum个数中选择k-(n-sum)个数变为d的倍数,这样总共就有

(M/d)nsumCkn+sumsum(M/d1)kn+sum

然后再把所有的gcd(d*2),…,gcd(d*s)减掉,就是gcd(d)的个数,
先预处理一下组合数的计算,然后从后往前算,这样复杂度为 n/1+n/2+n/3+...+n/n=nlog(n)

ACcode

#include <iostream>#include<cstdio>#include<cstring>#include<algorithm>#define Debug(x) cout<<(x)<<endlusing namespace std;typedef long long LL;const int MOD = 1e9+7;const int maxn = 3e5+10;LL ans[maxn];LL fact[maxn];LL fact_inv[maxn];int num[maxn];LL power_mod(LL x,LL n){    LL res = 1;    while(n)    {        if(n&1)res = res*x%MOD;        x = x*x%MOD;        n>>=1;    }    return res;}void init(){    fact_inv[0] = fact[0] = 1;    for(int i=1 ; i<maxn ; ++i)    {        fact[i] = fact[i-1]*i%MOD;        fact_inv[i] = power_mod(fact[i],MOD-2);    }}LL cal(int n,int m){    return (fact[n]*fact_inv[m]%MOD)*fact_inv[n-m]%MOD;}int main(){   //freopen("H:\\c++\\file\\stdin.txt","r",stdin);    int n,m,k;    init();    while(scanf("%d%d%d",&n,&m,&k)!=EOF)    {        memset(num,0,sizeof(num));        for(int i = 0 ; i<n ; ++i)        {            int x;            scanf("%d",&x);            num[x]++;        }        for(int i = m ; i>=1 ; --i)        {            int sum = 0;            for(int j = i ; j<=m ; j+=i)                sum+=num[j];            if(n-sum>k){                ans[i] = 0 ;                continue;}            ans[i] = (power_mod(m/i,n-sum)*power_mod(m/i-1,k-n+sum%MOD)%MOD)*cal(sum,k-n+sum)%MOD;            for(int j = 2*i ; j<=m ; j+=i)                ans[i]=(ans[i]-ans[j]+MOD)%MOD;        }        cout<< ans[1];        for(int i = 2 ; i<=m ; ++i)            printf(" %I64d",ans[i]);            printf("\n");    }    return 0;}
0 0