HDU - 4675(莫比乌斯反演)

Alice is playing a game with Bob.
Alice shows N integers a 1, a 2, …, a N, and M, K. She says each integers 1 ≤ a i ≤ M.
And now Alice wants to ask for each d = 1 to M, how many different sequences b 1, b 2, …, b N. which satisfies :
1. For each i = 1…N, 1 ≤ b i ≤ M
2. gcd(b 1, b 2, …, b N) = 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(x 1, x 2, …, x n) is the greatest common divisor of x 1, x 2, …, x n

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: a 1, a 2, ..., a n (1 ≤ a i ≤ 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

老套路了。。。

设 f(d) 为 gcd 为 d 的数对的对数,F(d)为gcd是d的倍数的数对的对数,

那么此处F(d)如何计算:

我们设数组 a[ i ]  的 n 个数中,是 d  (就是条件二要求的那个 gcd)的倍数的有 sum 个,那么不是 d 的倍数的有 n-sum 个

那么这 n - sum 个 a[ i ] 是绝对要使得 b[ i ] ! = a[ i ] 的

然后还剩下 sum 个 a[ i ] ,我们可以从中选择 k - (n - sum)个 a[ i ] 使得 b[ i ] ! = a[ i ],然后其余的 a[ i ] 都要使得 a[ i ] = b[ i ]

所以 F(d)= C(sum , k-( n - sum) )*pow( [ m/d ]  , n-sum )*pow( [ m/d ] - 1 , k - ( n - sum ) ) ([ m / d ] 向下取整)

至于为什么要减一。。。那 n - sum 个 a[ i ] 因为不是 d 的倍数,所以不会包含在 [ m / d ]*d 里面,在这里面选择 b[ i ] 的时候就不用减一了

另外 sum 个 a[ i ] 是 d 的倍数,所以会包含在 [ m / d ]*d 里面,所以在里面选择 b[ i ] 使得 b[ i ] ! = a[ i ] 的时候要减一。

然后就函数求和加加减减

下面代码中 sum[ i ] 表示 i 的倍数有多少个

这个题由于求 F(d)的形式比较特殊,所以没法分段求和来加一波速(大概?)

不过暴力能过

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
typedef long long LL;
const LL MOD=1e9+7;
#define fi first
#define se second
const int MAXN=500000+23;
LL prime[MAXN],total,miu[MAXN];
bool isprime[MAXN];
void make()
{
    int m=MAXN-3;
    memset(isprime,true,sizeof(isprime));
    total=0;
    isprime[0]=isprime[1]=false;
    miu[1]=1;
    for(int i=2;i<=m;i++)
    {
        if(isprime[i]){prime[++total]=i;miu[i]=-1;}
        for(int j=1;j<=total&&prime[j]*i<=m;j++)
        {
            isprime[i*prime[j]]=false;
            if(i%prime[j])
            {
                miu[i*prime[j]]=-miu[i];
            }
            else
            {
                miu[i*prime[j]]=0;
                break;
            }
        }
    }
    miu[0]=0;
//    for(int i=1;i<=m;i++)miu[i]+=miu[i-1];
}

LL n,m,k;
LL jc[MAXN];

LL quick(LL x,LL a)
{
    LL res=1;
    while(a)
    {
        if(a&1)res=res*x%MOD;
        x=x*x%MOD;
        a>>=1;
    }
    return res%MOD;
}
LL C(LL a,LL b)
{
    if(b==a||b==0)return 1;
    LL r1=jc[a],r2=jc[b]*jc[a-b]%MOD;
    r2=quick(r2,MOD-2);
    return r1*r2%MOD;
}

LL a[MAXN],sum[MAXN],F[MAXN];
LL vis[MAXN];
int main()
{
    make();
    jc[0]=1;
    for(int i=1;i<=MAXN-100;i++)
    {
        jc[i]=jc[i-1]*i%MOD;
    }
    while(scanf("%lld%lld%lld",&n,&m,&k)!=-1)
    {
        memset(sum,0,sizeof(sum));
        memset(vis,0,sizeof(vis));
        for(int i=0;i<n;i++)scanf("%lld",&a[i]),vis[a[i]]++;
        //sum[1]=n;
        for(LL i=1;i<=m;i++)
        {
            for(LL j=i;j<=m;j+=i)
            {
                if(vis[j])sum[i]+=vis[j];
            }
        }
        for(LL i=1;i<=m;i++)
        {
            LL ans=0;
            LL b=n-sum[i];
            LL z=m/i;
            if(b<=k)
            {
               F[i]=(quick(z,b)*quick(z-1,k-b)%MOD)*C(sum[i],k-b)%MOD;
            }
            else
            {
               F[i]=0;
            }
        }
        for(int i=1;i<=m;i++)
        {
            LL ans=0;
            if(F[i]==0)ans=0;
            else for(int j=i;j<=m;j+=i)
            {
                ans+=miu[j/i]*F[j];
                ans%=MOD;
            }
            printf("%lld",(ans+MOD)%MOD);
            if(i<m)putchar(' ');
        }
        puts("");
    }
    return 0;
}
emmmmm。。。。。。。大概就这样了吧

本人蒟蒻,如有错误,还望指出


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值