hdoj 6053(2017 Multi-University Training Contest - Team 2) TrickGCD

题目链接:TrickGCD

题目大意:给出一个长度为n的数组,让你构造出一个长度也为n的B数组,且B数组需要满足对于所有的1<=i<=n,A[i]<=B[i],且对于B数组,任意一个区间的gcd>=2,求满足的方案数

题目思路:转化一下就变成了B数组整个区间的gcd>=2,然后直接枚举这个gcd从2到min(a[i]),然后容斥算一下贡献:

当x为奇数个不同素数的积时,ans+=1*它对答案的贡献

当x为偶数个不同素数的积时,ans+=-1*它对答案的贡献

其余情况 ans+= 0*它对答案的贡献

容斥的时候发现刚好是莫比乌斯函数的相反数,然后直接套莫比乌斯函数上去,然后预处理一下每个数的贡献,贡献实际上是
(a[i]/x)(a[i]/x)(a[i]/x)(a[i]/x)(a[i]/x)(a[i]/x)
x为我们枚举的gcd,然后发现在某一段数的区间num/x是一致的,分块小优化一下,然后预处理每个数出现的次数,然后累加用快速幂算,然后贡献乘莫比乌斯函数就好了,具体看代码,很好的题目,num数组记得看大,不然会越界


#include <map>
#include <set>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
const int mod = 1e9+7;

ll prime[maxn],mob[maxn],vis[maxn],tot;
ll t,n,a[maxn],cnt[maxn],num[3*maxn];

void Mobius(){
    memset(prime,0,sizeof(prime));
    memset(mob,0,sizeof(mob));
    memset(vis,0,sizeof(vis));
    mob[1] = 1;
    tot = 0;
    for(ll i = 2;i < maxn; i++){
        if(!vis[i]){
            prime[tot++] = i;
            mob[i] = -1;
        }
        for(ll j = 0;j < tot&&i*prime[j] < maxn;j++){
            vis[i*prime[j]] = 1;
            if(i%prime[j]) mob[i*prime[j]] = -mob[i];
            else{
                mob[i*prime[j]] = 0;
                break;
            }
        }
    }
}

ll quick_mod(ll a,ll b,ll mod)//快速幂
{
    ll ans = 1;
    while(b)
    {
        if(b&1)
            ans = (ans*a)%mod;
        a = (a*a)%mod;
        b >>= 1;
    }
    return ans;
}


int main(){
    scanf("%lld",&t);
    Mobius();
    for(int Case = 1;Case <= t;Case++){
        scanf("%lld",&n);
        memset(cnt,0,sizeof(cnt));
        ll Min = 0x3f3f3f3f,Max = -1;
        for(ll i = 1;i <= n;i++){
            scanf("%lld",&a[i]);
            Min = min(Min,a[i]);
            Max = max(Max,a[i]);
            cnt[a[i]]++;
        }
        num[0] = 0;
        for(ll i = 1;i <= 2*Max;i++) num[i] = num[i-1]+cnt[i];

        ll ans = 0;
        for(ll i = 2;i <= Min;i++){
            ll tmp = 1;
            for(ll j = 1;j*i <= Max;j++){
                tmp = (tmp*quick_mod(j,num[j*i+i-1]-num[j*i-1],mod))%mod;
            }
            ans = (ans-tmp*mob[i]+mod)%mod;
        }
        printf("Case #%d: %lld\n",Case,ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值