首页 > 技术文章 > HDU 6053 TrickGCD(莫比乌斯反演)

zyb993963526 2017-07-28 08:22 原文

http://acm.hdu.edu.cn/showproblem.php?pid=6053

题意:
给出一个A数组,B数组满足Bi<=Ai。

现在要使得这个B数组的GCD值>=2,求共有多少种情况。

 

思路:
在比赛的时候筛了素数表,然后枚举GCD来做,但是还是有些重复情况无办法剔除,赛后才知道是要用莫比乌斯来处理的,然后就趁机学习了一下莫比乌斯。

先是枚举GCD,每个数的可选情况就是GCD/a【i】,在这里我们可以把GCD/a【i】相同的数一起处理,也就是用快速幂来计算,这样会显得更快。

那么莫比乌斯是用来干嘛的呢?

然后就是容斥思想。

 

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<sstream>
  6 #include<vector>
  7 #include<stack>
  8 #include<queue>
  9 #include<cmath>
 10 #include<map>
 11 #include<set>
 12 using namespace std;
 13 typedef long long ll;
 14 typedef pair<int,int> pll;
 15 const int INF = 0x3f3f3f3f;
 16 const int maxn = 1e5 + 5;
 17 
 18 const int mod= 1e9+7;
 19 
 20 int n;
 21 int a[maxn];
 22 
 23 ll cnt[maxn];
 24 ll num[3*maxn];
 25 
 26 bool check[maxn+1000];
 27 int prime[maxn+1000];
 28 int mu[maxn+1000];
 29 
 30 void Moblus()
 31 {
 32     memset(check,false,sizeof(check));
 33     mu[1] = 1;
 34     int tot = 0;
 35     for(int i = 2; i <= maxn; i++)
 36     {
 37         if( !check[i] ){
 38             prime[tot++] = i;
 39             mu[i] = -1;
 40         }
 41         for(int j = 0; j < tot; j++)
 42         {
 43             if(i * prime[j] > maxn) break;
 44             check[i * prime[j]] = true;
 45             if( i % prime[j] == 0){
 46                 mu[i * prime[j]] = 0;
 47                 break;
 48             }else{
 49                 mu[i * prime[j]] = -mu[i];
 50             }
 51         }
 52     }
 53 }
 54 
 55 ll qpow(ll a, ll b)
 56 {
 57     ll ans=1;
 58     while(b)
 59     {
 60         if(b&1)  ans=(ans*a)%mod;
 61         a=(a*a)%mod;
 62         b>>=1;
 63     }
 64     return ans;
 65 }
 66 
 67 int main()
 68 {
 69     //freopen("in.txt","r",stdin);
 70     Moblus();
 71     int kase=0;
 72     int T;
 73     scanf("%d",&T);
 74     while(T--)
 75     {
 76         memset(cnt,0,sizeof(cnt));
 77         scanf("%d",&n);
 78         int MIN=INF;
 79         int MAX=0;
 80         for(int i=1;i<=n;i++)
 81         {
 82             scanf("%d",&a[i]);
 83             MIN=min(MIN,a[i]);
 84             MAX=max(MAX,a[i]);
 85             cnt[a[i]]++;
 86         }
 87 
 88         num[0]=0;
 89         for(int i=1;i<=200000;i++)   num[i]=num[i-1]+cnt[i];
 90 
 91         ll ans=0;
 92 
 93         for(int i=2;i<=MIN;i++)
 94         {
 95             ll tmp=1;
 96             for(int j=1;j*i<=MAX;j++)
 97             {
 98                 tmp=(tmp*qpow((ll)j,num[i*j+i-1]-num[i*j-1]))%mod;  //快速幂计算,j是GCD的倍数,也就是a【i】/GCD
 99             } 
100             ans=(ans-tmp*mu[i]+mod)%mod;  
101         }
102 
103         printf("Case #%d: %I64d\n",++kase,ans);
104     }
105     return 0;
106 }

 

推荐阅读