牛客寒假训练第四场-J

线性筛+暴力分解质因数+快速幂

题目传送门

首先对于\(gcd(x_1, x_2, ..., x_n)\)有两种解法

  1. 辗转相除法,每次用辗转相除法求两个数的gcd,然后用结果再和下一个数求gcd即可
  2. 对每个\(x_i\) 分解质因子,n个数的最大公倍数就是:n个数所有(公共质因子的最小次幂 )的积

对于方法2,举个例子:

\(12 = 2^2*3\)

\(8 = 2^3\)

\(18=2*3^2\)

\(10=2*5\)

那么这三个数的最大公因数就是:\(2^{min(2,3,1,1)}*3^{min(1,0,2,0)}*5^{min(0,0,0,1)} = 2^1 = 2\)


由于\((x^a)^b = x^{a*b}\)

所以假设 \(x_i\)可以质数分解成为:\(x_i = p_{i1}^{k_{i1}}* p_{i2}^{k_{i2}}*...* p_{it}^{k_{it}}\);其中\(p_{ij}\)表示分解的质数, \(k_{ij}\)表示该质数的幂

\(x_i^{b_i} = p_{i1}^{k_{i1} * b_i}* p_{i2}^{k_{i2}*b_i}*...* p_{it}^{k_{it}*b_i}\)

其中质数分解,直接暴力分解即可。

我们的任务就是找出每个质数最小的幂(没有这个质数,幂就是0),然后用快速幂相乘即可。

需要注意的是,对任意一个数分解质因子时,要求出1-10000所有质数的幂,比如 \(10= 2^1*3^0*5^1*7^0*11^0*...\) ,因为这里的0次也是有用的,表示n个数该质因子最小幂次是0,不能省略掉。

ac代码

// 分解质因数,求每个数所有质因数及其幂次 
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int mod = 1e9+7; 
int cnt=0;
int vis[10010]; //
int x[10010], p[10010], prime[10010]; // prime存质数
int num[10010]; // 存每个质数的最小幂次,初始化成0x7f7f7f7f; num[i]表示prime[i]的最小幂次
int n;
// 快速幂 
ll ksm(int n, int k)
{
	ll ans = 1;
	ll tot = n;
	while(k){
		if(k&1) ans=ans*tot%mod;
		tot = tot*tot%mod;
		k>>=1;
	}
	return ans%mod;
}
// 打印质数表 
void table(int n)
{
	memset(vis, 0, sizeof(vis));
	for(int i=2; i<=n; ++i){
		if(!vis[i]) 
			prime[cnt++]=i;
		for(int j=0; j<cnt && 1ll*i*prime[j]<=n; ++j){
			vis[i*prime[j]] = 1;
			if(i%prime[j]==0) break;
		}
	}
}
int main()
{
	table(10000);
	scanf("%d",&n);
	for(int i=0; i<n; ++i){
		scanf("%d", &x[i]); 
	}
	for(int i=0; i<n; ++i){
		scanf("%d", &p[i]); 
	}
	memset(num, 0x7f7f7f7f, sizeof(num));
	for(int i=0; i<n; ++i){
		for(int j=0; j<cnt; ++j){ // 暴力分解质因数即可; 要分解出所有的质因数,包括幂次 为0的 
//			if(x[i] == 1) break;
//			if(x[i]%prime[j]!=0) continue;
			int t=0;
			while(x[i]%prime[j]==0) {
				x[i]/=prime[j];
				t++;
			}
			num[j] = min(num[j], t*p[i]);
			
		}
	}
	ll ans=1;
	for(int i=0; i<cnt; ++i){
		if(num[i]==0) continue;
		ans = ans*ksm(prime[i], num[i])%mod;
	}
	printf("%lld", ans);
}

时间复杂度:\(O(n)\)

posted @ 2021-02-20 23:54  VanHope  阅读(43)  评论(0编辑  收藏  举报