hdoj 6053(2017 Multi-University Training Contest

来源:互联网 发布:天天酷跑ios淘宝充值 编辑:程序博客网 时间:2024/06/06 20:59

题目链接: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;}