Loading

2021牛客寒假算法基础集训营4 J. 邬澄瑶的公约数(GCD/唯一分解定理)

链接:https://ac.nowcoder.com/acm/contest/9984/J
来源:牛客网

众所周知,邬澄瑶正在学习欧几里得算法。

现在她已经可以轻松求解 gcd(x1,⋯,xn),并为此洋洋得意。为了整治狂妄自大的邬澄瑶,她的室友把$gcd(x_{1}{p_1},⋯,x_{n}) $这个式子甩给了他。

邬澄瑶被难住了,只好来求助于你,希望你帮她求出这个式子。
由于结果可能很大,你需要对 1e9+7 取模。
特别的,邬澄瑶的室友认为 gcd⁡(x)=x 。

输入描述:

第一行一个数表示 n 。

第二行 n 个数,第 i 个数表示 xixi 。

第三行 n 个数,第 i 个数表示 pipi 。

其中,1≤n,xi,pi≤1e41≤n,xi,pi≤1e4 。

输出描述:

输出一行一个数表示答案。

示例1

输入

复制

2
9 3
1 2

输出

复制

9

直接暴力显然不可取,考虑gcd的性质:

\(a=p_1^{a1}p_2^{a2}p_3^{a3}... b = p_1^{b1}p_2^{b2}p_3^{b3}\),联想唯一分解定理,则\(gcd(a, b) = p_1^{min(a1,b1)}p_2^{min(a2,b2)}p_3^{min(a3,b3)}...\)

拓展到多个数的gcd同样成立。对于本题,要求的是多个幂次的gcd,其实只需要在取min的时候给质数的指数乘上次幂。比如设\(a=p_1^{a1}p_2^{a2}p_3^{a3}... b = p_1^{b1}p_2^{b2}p_3^{b3}\),联想唯一分解定理,则\(gcd(a^s, b^t) = p_1^{min(a1\times s,b1\times t)}p_2^{min(a2\times s,b2\times t)}p_3^{min(a3\times s,b3\times t)}...\)

观察到数的范围是1e4,因此开一个数组factor,factor[i]代表i这个质数(如果是质数的话,不是质数幂次为0自然没有影响)的幂次(在gcd中)。对于题目的每个x先进行唯一分解,根据分解得到的质数pi来更新factor数组,即factor[pp[i]] = min(factor[pp[i]], cc[i] * np);cc[i]就是x分解后得到的当前这个质数的次数,np是x原本的次数。当然如果当前factor[i]是0的话就直接赋值,不是0再取min。最后用快速幂计算gcd即可:\(GCD = \Sigma_{i = 1}^{10000}i^{factor[i]}\)

但直接这么写会有问题:比如某个x其分解后的质因子不包含5,而factor[5]已经在遍历到别的x的时候被更新为非0数了,这时候根据上面公式应该把factor[5]设置为0,但如果对于每个x都要把范围内所有质数判断一遍的话显然会爆炸。因此可以再开一个cnt数组,cnt[i]代表所有x里质因子包含i这个质数(如果是质数的话)的x的个数。factor[i]不为0当且仅当cnt[i] == n(即输入的x的总个数),最后遍历计算的时候判断一下即可。复杂度是\(n\sqrt{n}\)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define mod 1000000007
using namespace std;
long long x[10005], p[10005];
int n;
long long fpow(long long aa, long long bb)
{
	long long ans = 1;
	for(; bb; bb >>= 1)
	{
		if(bb & 1) ans = ans * aa % mod;
		aa = aa * aa % mod;
	}
	return ans;
}
long long pp[10005], cc[10005];//唯一分解定理板子,pp存分解出来的质数,cc存对应的次幂
int m = 0;
long long factor[10005], cnt[10005];
void divide(long long nn, long long np)
{
	m = 0;
	for(long long i = 2; i <= sqrt(nn); i++)
	{
		if(nn % i == 0)
		{
			pp[++m] = i, cc[m] = 0;
			while(nn % i == 0) nn /= i, cc[m]++;
		}
	}
	if(nn > 1) pp[++m] = nn, cc[m] = 1;
	for(int i = 1; i <= m; i++)//分解完后更新factor数组
	{
		if(factor[pp[i]]) factor[pp[i]] = min(factor[pp[i]], cc[i] * np);
		else factor[pp[i]] = cc[i] * np;
		cnt[pp[i]]++;
	}
}
int main()
{
	freopen("data.txt", "r", stdin);
	memset(factor, 0, sizeof(factor));
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> x[i];
	for(int i = 1; i <= n; i++) cin >> p[i];
	for(int i = 1; i <= n; i++) divide(x[i], p[i]);
	long long ans = 1;
	for(int i = 2; i <= 10000; i++)
		if(cnt[i] == n) 
			ans = ans * fpow(i, factor[i]) % mod;
	cout << ans;
	return 0;
}
posted @ 2021-02-19 19:37  脂环  阅读(204)  评论(0编辑  收藏  举报