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


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

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

    现在她已经可以轻松求解 gcd(x1,⋯,xn),并为此洋洋得意。为了整治狂妄自大的邬澄瑶,她的室友把$gcd(x_{1}{p_1},⋯,x_{n}{p_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 imes s,b1 imes t)}p_2^{min(a2 imes s,b2 imes t)}p_3^{min(a3 imes s,b3 imes 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的总个数),最后遍历计算的时候判断一下即可。复杂度是(nsqrt{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;
    }
    
  • 相关阅读:
    ASP.NET HttpRuntime.Cache缓存类使用总结
    ASP.NET MVC自定义AuthorizeAttribute篇知识点讲解—登录限制
    Echarts图表控件使用总结2(Line,Bar)—问题篇
    数据库查询实例(包含所有where条件例子)
    php file_get_contents读取大容量文件方法
    如何给mysql用户分配权限
    dedecms {dede:php}标签用法介绍
    js获取字符串最后一个字符代码
    CSS3选择器之学习笔记
    SQL中实现SPLIT函数几种方法
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/14417757.html
Copyright © 2020-2023  润新知