hdu 6053 TrickGCD (莫比乌斯反演)

题目链接:哆啦A梦传送门

题意:给出长度为n的序列a,找出长度也为n的序列b,
满足 b i ≤ a i , g c d ( b 1 , b 2 , . . . , b n ) ≥ 2 b_i\leq a_i,gcd(b_1,b_2,...,b_n)\geq 2 biai,gcd(b1,b2,...,bn)2,问:有多少种不同的序列b?

题解:

我们先设:
f ( d ) f(d) f(d)为满足 g c d ( b 1 , b 2 , . . . , b n ) = d gcd(b_1,b_2,...,b_n)=d gcd(b1,b2,...,bn)=d的方案数。

那么我们根据莫比乌斯第二描述:
F ( n ) = ∑ n ∣ d f ( d ) F(n)=\sum_{n|d}f(d) F(n)=ndf(d)
f ( n ) = ∑ n ∣ d u ( d n ) ∗ F ( d ) f(n)=\sum_{n|d}u(\frac{d}{n})*F(d) f(n)=ndu(nd)F(d)

易得: F ( n ) F(n) F(n)为满足 d ∣ g c d ( b 1 , b 2 , . . . , b n ) d|gcd(b_1,b_2,...,b_n) dgcd(b1,b2,...,bn)的方案数。

参考博客:(https://blog.csdn.net/V5ZSQ/article/details/76381584)

即: F ( d ) = ∏ i = 1 m i ⌊ a i d ⌋ \begin{aligned}F(d)=\prod_{i=1}^{mi} \lfloor \frac{a_i}{d}\rfloor \end{aligned} F(d)=i=1midai ,显然这样求复杂度有点大,我们再设 s u m [ i ] 表 示 a i ≤ i 的 总 数 sum[i]表示a_i \leq i的总数 sum[i]aii,对于每个d,
它在每一段 a ∈ ( i ∗ d − 1 , ( i + 1 ) ∗ d − 1 ) a\in(i*d-1,(i+1)*d-1) a(id1,(i+1)d1) ( i ∗ d ≤ 1 e 5 i*d\leq 1e5 id1e5)的值除d的值都是一样的,都为i。那么我们只需算下它们的幂 i s u m [ ( i + 1 ) ∗ d − 1 ] − s u m [ i ∗ d − 1 ] i^{sum[(i+1)*d-1]-sum[i*d-1]} isum[(i+1)d1]sum[id1]就好了。

那么此题的结果就为:

a n s = F ( 1 ) − f ( 1 ) = F ( 1 ) − ∑ d = 1 m i F ( d ) = − ∑ d = 2 m i F ( d ) \begin{aligned} ans&=F(1)-f(1)\\ &=F(1)-\sum_{d=1}^{mi}F(d)\\ &=-\sum_{d=2}^{mi}F(d) \end{aligned} ans=F(1)f(1)=F(1)d=1miF(d)=d=2miF(d)

#include<bits/stdc++.h>

using namespace std;

typedef long long LL;

const int N=1e5+10;
const LL mod=1e9+7;

int prime[N],tot,mu[N];

bool vis[N];

void init()
{
    mu[1]=1;

    for(int i=2;i<N;i++)
    {
        if(!vis[i]){
            prime[++tot]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=tot&&i*prime[j]<N;j++){
            int x=i*prime[j];

            vis[x]=1;
            if(i%prime[j]==0){
                mu[x]=0;
                break;
            }
            mu[x]=-mu[i];

        }

    }

}


int sum[N];

LL F[N];

LL fast(LL x,LL y)
{
    LL re=1;
    while(y)
    {
        if(y&1) re=re*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return re;
}


int main()
{

    init();
    int n,T=0;
    int ncase;
    scanf("%d",&ncase);
    while(ncase--){

        int n;
        scanf("%d",&n);
        int mi=1e5,item;
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=n;i++){
            scanf("%d",&item);
            mi=min(item,mi);
            sum[item]++;
        }

        for(int i=1;i<=1e5;i++){
            sum[i]+=sum[i-1];
        }

        for(int d=2;d<=mi;d++)
        {
            F[d]=1LL;
            for(int i=1;i*d<=100000;i++){
                F[d]=F[d]*fast(i,sum[min((i+1)*d-1,100000)]-sum[i*d-1])%mod;
//            printf("%d %d %d %d\n",i,d,mi,F[d]);
            }

        }


        LL ans=0;
        for(int d=2;d<=mi;d++)
        {
            ans=ans-mu[d]*F[d]+mod;
            ans%=mod;
        }
        printf("Case #%d: %lld\n",++T,ans);




    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值