HDU6053 TrickGCD【数学】

题意: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;

}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值