HDU4675 GCD of Sequence(預處理階乘逆元+推公式)

bigbigship發表於2015-08-15

題目連結:傳送門 

題意:

給定一個長度為n的序列a,且 1<=a[i]<=m,求有多少個序列b,使得GCD(b[1],b[2],...b[n])=x (1<=x<=m),且

正好有k個b[i]!=a[i].

分析:

設F[d]表示 gcd = d的滿足條件的序列的個數,G[d]表示滿足條件的gcd = k*d (1<=k)的序列的個數。那麼很明顯 F[d] = G[d] - F[2*d] -F[3*d]...

在做的時候我們先求G[d]我們列舉gcd,在原來的序列中找到gcd倍數的個數,設為tot.因為題目要求恰好與k個不同而且,gcd = d,那麼新的序列肯定都得是d的倍數,原序列中不是d的倍數的個數為 n-tot.這些數十肯定需要修改的,如果

1)n-tot>k那麼肯定是不可能的了。

2)n-tot<=k 那麼這n-tot個數要換成d的倍數,每個數有m/d種,所有的就是 (m/d)^(n-tot),剩下的tot個數中還要選出k-(n-tot)個數變成不等於他們自己本身的d的倍數,每個數有(m/d-1)種,那麼這種情況有

C(tot,k-(n-tot))*(m/d-1)^(k-(n-tot))種。G[d]=(m/d)^(n-tot)*C(tot,k-(n-tot))*(m/d-1)^(k-(n-tot))%mod

在求組合數的時候我們要先預處理一下階乘的逆元。因為資料量比較大我們需要用遞推法來預處理逆元

inv[n] = inv[mod%n]*(mod-mod/n)%mod;具體的證明:傳送門 

程式碼如下:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long LL;

const LL mod = 1e9+7;

const int maxn = 3e5+10;

LL ans[maxn];
LL cnt[maxn];
LL inv[maxn];
LL fac[maxn];

LL get_inv(LL n){
    if(n==1) return 1;
    return get_inv(mod%n)*(mod-mod/n)%mod;
}

LL Com(LL n,LL m){
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}

void init(){
    inv[0]=fac[0]=1;
    for(int i=1;i<maxn;i++){
        fac[i]=fac[i-1]*i%mod;
        inv[i]=get_inv(fac[i]);
    }
}

LL quick_mod(LL a,LL b){
    LL ans = 1;
    while(b){
        if(b&1) ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ans;
}

int main(){
    init();
    int n,m,k;
    while(~scanf("%d%d%d",&n,&m,&k)){
        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<n;i++){
            int x;
            scanf("%d",&x);
            cnt[x]++;
        }
        for(int i=m;i>=1;i--){
            LL tot = 0;
            for(int j=i;j<=m;j+=i) tot+=cnt[j];
            if(n-tot>k){
                ans[i]=0;
                continue;
            }
            ans[i]=quick_mod(m/i,n-tot)*quick_mod(m/i-1,k-(n-tot))%mod;
            ans[i]=ans[i]*Com(tot,k-(n-tot))%mod;
            for(int j=i+i;j<=m;j+=i)
                ans[i]=(ans[i]-ans[j]+mod)%mod;
        }
        for(int i=1;i<m;i++)
           printf("%I64d ",ans[i]);
        printf("%I64d\n",ans[m]);
    }
    return 0;
}


















相關文章