解题报告: HDU_4675 GCD of Sequence 计数

来源:互联网 发布:贵阳大数据是什么 编辑:程序博客网 时间:2024/04/30 17:36

题目链接


题意:

给定长度为n的一个正整数序列,里面每个数的大小均为最大为m,让你构造出另一个等长的正整数序列要求满足一下三个条件:

1、数列里的每个数小于等于m。

2、整个数列的最大公因子为d。

3、你构造出的数列与原数列对应位置上的数刚好有k对不相等。

询问你当d 分别为 1~m 时,能构造出序列的种类数。

思路:

不好直接考虑计算最大公因子刚好为d的结果(设为f(d)),那么先计算最大公因子为d的倍数的结果(设为g(d)),那么可知,,其中u(i) 为反演系数,那么已知g函数的结果,我们就能在O(mlog(n))的时间求得答案。

条件三可以理解为从原数列中选k个数改变。

那么就有n-k个数的值不发生改变,如果这n-k个数中存在不是d的倍数的数,那么就无法得到要求的数列,此时结果为0。

设原数列中有x个数为d的倍数,那么:

① n-k > x :

 说明不变的数中一定有数不是d的倍数,此时结果为0

g[d] = 0

② n-k <= x : 

对于 (n-k) 个不是d的倍数的数 ,这些数一定要改变成小于等于m的d的倍数,它们有m/d中选择。

对于 剩下的  j = ( x - (n-k) )个原本就是d的倍数的数,这些数的选择有  C(x,j) 种,它们要变成除了它们自己以外的其余(m/d - 1)个数之一。

g[d] = pow(m/d , n-k ) * C(x,j) * pow(m/d -1 , j );


即可。

#include<bits/stdc++.h>#define pii pair<int,int>#define fi first#define se secondconst int mod = 1e9+7;using namespace std;int n,m,k;int A[300005];int mu[300005];int pr[33000];long long ni[300005];long long fac[300006];int isp[300005];int num[300005];long long ans[300005];vector<pii>V;vector<int>E[300005];long long qpow(long long x,int y){    long long res = 1;    while(y){        if(y&1)res = res * x % mod;        y>>=1;        x = x * x % mod ;    }return res;}void init(){    fac[0] = ni[0] = 1;    for(int i=1;i<=3e5;i++){        isp[i]=true;        fac[i] =(1LL*fac[i-1]*i%mod);        ni[i] = qpow(fac[i],mod-2);        for(int j=i;j<=3e5;j+=i){            E[j].emplace_back(i);        }    }    mu[1]=1;    int all = 0;    for(int i=2;i<=3e5;i++){        if(isp[i]){            mu[i] = -1;            pr[all++] = i;        }for(int j=0;j<all;j++){            long long t = 1LL * pr[j] * i;            if(t>3e5)break;            mu[t] = -mu[i];            isp[t] = false;            if(i%pr[j]==0){                mu[t] = 0;                break;            }        }if(mu[i]){            V.emplace_back(pii(i,mu[i]));        }    }}inline long long C(int n,int m){    if(n==m||m==0)return 1LL;    return 1LL*fac[n]*ni[n-m]%mod*ni[m]%mod;}int main(){    init();    while(scanf("%d%d%d",&n,&m,&k)==3){        memset(num,0,sizeof(num));        for(int i=1;i<=n;i++){            scanf("%d",&A[i]);            for(int j=0;j<E[A[i]].size();j++){                num[E[A[i]][j]]++;            }        }for(int i=1;i<=m;i++){            int cnt = m/i , j = k+num[i]-n;            if(n-num[i]<=k)ans[i] = 1LL * qpow(cnt,n-num[i]) * C(num[i],j) % mod * qpow(cnt-1,j) % mod;            else ans[i] = 0;        }for(int i=1;i<=m;i++){            for(int j=0;j<V.size();j++){                int t = V[j].fi * i ;                if(t<=m){                    ans[i] = ( ans[i] +  V[j].se * ans[t] + mod )% mod ;                }else {                    break;                }            }printf("%I64d%c",ans[i],i<m?' ':'\n');        }    }return 0;}




原创粉丝点击