HDU 4675 GCD of Sequence (莫比乌斯反演第二种形式+组合数学)*

GCD of Sequence

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 1979    Accepted Submission(s): 701


 

Problem Description

Alice is playing a game with Bob.
Alice shows N integers a1, a2, …, aN, and M, K. She says each integers 1 ≤ ai ≤ M.
And now Alice wants to ask for each d = 1 to M, how many different sequences b1, b2, …, bN. which satisfies :
1. For each i = 1…N, 1 ≤ b[i] ≤ M
2. gcd(b1, b2, …, bN) = d
3. There will be exactly K position i that ai != bi (1 ≤ i ≤ n)

Alice thinks that the answer will be too large. In order not to annoy Bob, she only wants to know the answer modulo 1000000007.Bob can not solve the problem. Now he asks you for HELP!
Notes: gcd(x1, x2, …, xn) is the greatest common divisor of x1, x2, …, xn

 

Input

The input contains several test cases, terminated by EOF.
The first line of each test contains three integers N, M, K. (1 ≤ N, M ≤ 300000, 1 ≤ K ≤ N)
The second line contains N integers: a1, a2, ..., an (1 ≤ ai ≤ M) which is original sequence.

 

 

Output

For each test contains 1 lines :
The line contains M integer, the i-th integer is the answer shows above when d is the i-th number.

 

Sample Input

 

3 3 3 3 3 3 3 5 3 1 2 3

 

Sample Output

 

7 1 0 59 3 0 1 1

Hint

In the first test case : when d = 1, {b} can be : (1, 1, 1) (1, 1, 2) (1, 2, 1) (1, 2, 2) (2, 1, 1) (2, 1, 2) (2, 2, 1) when d = 2, {b} can be : (2, 2, 2) And because {b} must have exactly K number(s) different from {a}, so {b} can't be (3, 3, 3), so Answer = 0

 

Source

2013 Multi-University Training Contest 7

 

Recommend

zhuyuanchen520   |   We have carefully selected several similar problems for you:  6437 6436 6435 6434 6433 

 

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define read(x,y) scanf("%d%d",&x,&y)

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ll long long

const int  maxn =3e5+5;
const int mod=1e9+7;
/*
题目大意:给定n,m,k,
和n个数,m是b序列每个数的上界,计数b序列的个数,
b序列满足:gcd{b}=d,b序列中恰好有k个和n个数不同。
对d到1和n的取值,计数并输出。

这是另一种莫比乌斯反演的应用。
我们知道普通的莫比乌斯反演sigma对象是因子,
还有一种形式是倍数的sigma ,
我不大会打字公式。。。真是抱歉。。。

令F:gcd为d 的倍数的序列个数,
f:gcd恰好为d 的序列个数。
那么通过上面的莫比乌斯,如果我们求出每一个F就可以得到f.

如何求F呢,看到n个区间范围的形式还是老套路,
离散化计数,然后对这些数进行分类,
首先肯定要从原序列中抽取n-k个数,那么肯定抽取d的倍数,
抽取方式有C(tot,n-k),tot代表有多少个d的倍数在原序列中。
那么剩余的可选的,可以同过快速幂计数,但要注意,
在tot-n+k中,也就是剩余的虽然是d的倍数但是没选到的数中,M/d要减一,因为不能选。

那么下面就是一系列数论模板满天飞的时候了。。。。


*/

ll powmod(ll x,ll y){ll t;for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod;return t;}

///筛法求莫比乌斯函数
int prim[maxn],vis[maxn],tot=0;
int miu[maxn];
void sieve()
{
    ///memset(vis,0,sizeof(vis));
    ///memset(miu,0,sizeof(miu));
    miu[1]=1;///一点点失误都不行...
    int N=maxn-1;
    for(int i=2;i<=N;i++)
    {
        if(vis[i]==0)
        {
            miu[i]=-1;
            prim[tot++]=i;
        }
        for(int j=0;j<tot;j++)
        {
            int k=prim[j]*i;
            if(k>N) break;
            vis[k]=1;
            if(i%prim[j]) miu[k]=-miu[i];
            else break;
        }
    }
}

int n,m,k,x;
int cnt[maxn<<1];

ll F[maxn];///f为答案数组
///阶乘逆元和组合数
ll fac[maxn],inv[maxn];///注意数据范围
/*扩展欧几里得算法,求逆元和gcd
void exgcd(ll a,ll b,ll &d,ll &x,ll &y)
{
    if(b==0)
    {
        d=x;
        x=1;
        y=0;
        return ;
    }
    exgcd(b,a%b,d,y,x);
    y-=x*(a/b);
}
ll Inv(ll a,ll n){
    ll d,x,y;
    exgcd(a,n,d,x,y);
    return d == 1 ? (x+n)%n : -1;
}
*/
void init()
{
    fac[0]=inv[0]=1;
    for(int i=1;i<maxn;i++)   fac[i]=fac[i-1]*i%mod;
   inv[maxn-1]=powmod(fac[maxn-1],mod-2)%mod;
   for(int i=maxn-2;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
inline ll C(ll n,ll m)
{
    if(m>n) return 0;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}


int main()
{
    sieve();
    init();
    while(~scanf("%d%d%d",&n,&m,&k))
    {
        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<n;i++)
        {
            scanf("%d",&x);
            cnt[x]++;
        }

        for(int i=1;i<=m;i++)
            for(int j=i+i;j<=m;j+=i)///O(nlogn)
                cnt[i]+=cnt[j];            ///记录d的倍数的个数,cnt(d)=tot

        for(int i=1;i<=m;i++)///求F函数
        {
            if(cnt[i]<n-k) {F[i]=0;continue;}
            ///原数列中i的倍数
            F[i]=C(cnt[i],n-k);///预处理组合数
            F[i]=F[i]*powmod(m/i,n-cnt[i])%mod*powmod(m/i-1,cnt[i]-n+k)%mod;
        }

        ll res=0;
        for(int i=1;i<=m;i++,res=0)
        {
            for(int j=i;j<=m;j+=i)  res=(res+(ll)miu[j/i]*F[j]%mod+mod)%mod;
            printf("%lld%c",res,(i==m)?'\n':' ');
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37451344/article/details/82017883
今日推荐