【2019/07/20测试 T1】最大公约数

传送门


Problem

给出 n n n 个数,计算其所有子集的 G C D GCD GCD(即子集所有数的 g c d gcd gcd)的乘积对 1 0 9 + 7 10^9+7 109+7 取模的结果。

其中空子集的 G C D GCD GCD 1 1 1

数据范围: 2 ≤ n , a i ≤ 1 0 5 2≤n,a_i≤10^5 2n,ai105


Solution

考场上一个细节没注意,硬生生把 100 100 100 分变成了 20 20 20 分。。。

先用桶排,算出每个数的出现次数。然后考虑枚举 g c d gcd gcd,假设当前枚举的数为 x x x,可以求出 x x x 的倍数个数 s u m x sum_x sumx,那么初步认为以 x x x g c d gcd gcd 的集合数 s e t x = ( 2 s u m x − 1 ) set_x=(2^{sum_x}-1) setx=(2sumx1) 个。

但是这样显然是不对的,举个例子,在枚举 2 2 2 的时候,这样会计算到 { 6 } , { 4 , 8 } \{6\},\{4,8\} {6},{4,8} 等集合。要想办法除去这些集合。

由于多算的集合的 g c d gcd gcd 肯定是 x x x 的倍数,因此我们枚举 x x x 的倍数然后减掉相应的 s e t set set 值即可,即以 x x x g c d gcd gcd 的子集数量为 s e t x − ∑ n ∣ d s e t d set_x-\sum_{n|d}set_d setxndsetd

由于我们要先知道所有倍数的 s e t set set 值才能推到 x x x,故应倒着循环。

最后的答案 a n s = ∏ i = 1 1 0 5 i s e t i ans=\prod_{i=1}^{10^5}i^{set_i} ans=i=1105iseti,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

有一个地方要注意,即 s e t i set_i seti 可能比较大,我们要先对它进行降幂操作。根据费马小定律,当 p p p 为质数时, a p − 1 ≡ 1 (   m o d    p ) a^{p-1}\equiv 1(\,\mathrm{mod}\;p) ap11(modp),因此要将 s e t i % ( p − 1 ) set_i\%(p-1) seti%(p1) 再用快速幂。


Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define Mod 1000000007
using namespace std;
int cnt[N],Set[N];
int Power(int a,int b,int p){
	int ans=1;
	for(;b;b>>=1,a=1ll*a*a%p)
		if(b&1)  ans=1ll*ans*a%p;
	return ans;
}
int main(){
	int n,x,ans=1;
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		scanf("%d",&x),cnt[x]++;
	for(int i=2;i<=1e5;++i){
		int sum=0;
		for(int j=i;j<=1e5;j+=i)  sum+=cnt[j];
		Set[i]=Power(2,sum,Mod-1)-1;
	}
	for(int i=1e5;i>=2;--i){
		if(!Set[i])  continue;
		for(int j=2*i;j<=1e5;j+=i)  Set[i]=(Set[i]-Set[j]+Mod-1)%(Mod-1);
		ans=1ll*ans*Power(i,Set[i],Mod)%Mod;
	}
	printf("%d",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值