HDU6053 TrickGCD【数学】

来源:互联网 发布:2017格里芬体测数据 编辑:程序博客网 时间:2024/05/29 10:22

题意:B≤A,B中所有元素gcd≥2,求B的方案数


思路:枚举所有2≤x≤min(A[i])作为gcd,对于 每个A[i],求它有几个gcd的倍数,把它们累乘,最后方案数累加。

但要去掉重复的数,比如6是2、3的倍数,用莫比乌斯反演,来每个gcd前乘个系数。

n最大1e5,取gcd n,遍历A n,O(n^2)超时的节奏。

这个用一个前缀和 和 快速幂优化。

比如gcd是2,A中元素用4、5,4、5两个数 都能取 2、4,累乘2*2,这个可以用快速幂

用前缀和记录元素的个数


#include<bits/stdc++.h>using namespace std;typedef long long ll;const int maxn = 1e5+5;const int MOD = 1e9+7;int a[2*maxn],sum[2*maxn];int check[maxn],mu[maxn],prime[maxn];void Moblus(){      memset(check,false,sizeof(check));      mu[1] = 1;      int tot = 0;      for(int i = 2; i <= maxn; i++)      {          if( !check[i] )          {              prime[tot++] = i;              mu[i] = -1;          }          for(int j = 0; j < tot; j++)          {              if(i * prime[j] > maxn) break;              check[i * prime[j]] = true;              if( i % prime[j] == 0)              {                  mu[i * prime[j]] = 0;                  break;              }              else              {                  mu[i * prime[j]] = -mu[i];              }          }      }  }ll kuai(ll a,ll b){ll res = 1;while(b){if(b&1) res = res * a % MOD;b >>= 1;a = a * a % MOD;}return res;}int main(void){int T,kase = 1,n,mm,i,j,ma;Moblus();scanf("%d",&T);while(T--){scanf("%d",&n);mm = 0x3f3f3f3f;ma = 0;memset(a,0,sizeof a);for(i = 1; i <= n; i++){int b;scanf("%d",&b);a[b]++;mm = min(mm,b);ma = max(ma,b);}sum[0] = 0;for(i = 1; i <= 2*maxn-1; i++)sum[i] = sum[i-1] + a[i];long long ans = 0ll;for(i = 2; i <= mm; i++) //gcd{long long temp = 1ll;for(j = 1; i*j <= ma; j++){temp *= kuai( (ll)j , (ll)sum[i*(j+1)-1] - sum[i*j-1] );temp %= MOD;}ans -= mu[i] * temp % MOD;ans = ( (ans%MOD)+MOD ) % MOD;}printf("Case #%d: %lld\n",kase++,ans);}return 0;}


原创粉丝点击