您的位置:首页 > 其它

HDU6053 TrickGCD【数学】

2017-07-28 14:06 239 查看
题意: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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: