Codeforces Round #641 (Div. 2) C. Orac and LCM(质因子分解)

写在前面

笔者是一名十八线蒟蒻ACMer,文中可能会有多处错误与疏漏,欢迎指出。

原题题面

原题地址
时间:3 seconds
空间:256M
For the multiset of positive integers s = s 1 , s 2 , … , s k s={s_1,s_2,…,s_k} s=s1,s2,,sk, define the Greatest Common Divisor (GCD) and Least Common Multiple (LCM) of s as follow:
g c d ( s ) gcd(s) gcd(s) is the maximum positive integer x x x, such that all integers in s s s are divisible on x x x.
l c m ( s ) lcm(s) lcm(s) is the minimum positive integer x x x, that divisible on all integers from s s s.
For example, g c d ( 8 , 12 ) = 4 , g c d ( 12 , 18 , 6 ) = 6 gcd({8,12})=4,gcd({12,18,6})=6 gcd(8,12)=4,gcd(12,18,6)=6 and l c m ( 4 , 6 ) = 12 lcm({4,6})=12 lcm(4,6)=12. Note that for any positive integer x x x, g c d ( { x } ) = l c m ( { x } ) = x gcd(\{x\})=lcm(\{x\})=x gcd({x})=lcm({x})=x.
Orac has a sequence a a a with length n n n. He come up with the multiset t = { l c m ( a i , a j ) ∣ i < j } t=\{lcm({a_i,a_j}) | i<j\} t={lcm(ai,aj)i<j}, and asked you to find the value of g c d ( t ) gcd(t) gcd(t) for him. In other words, you need to calculate the GCD of LCMs of all pairs of elements in the given sequence.
Input
The first line contains one integer n n n ( 2 ≤ n ≤ 1 e 5 ) (2≤n≤1e5) (2n1e5).
The second line contains n n n integers, a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an ( 1 ≤ a i ≤ 2 e 5 ) (1≤a_i≤2e5) (1ai2e5).
Output
Print one integer: g c d ( { l c m ( a i , a j ) ∣ i < j } ) gcd(\{lcm({a_i,a_j}) | i<j\}) gcd({lcm(ai,aj)i<j}).
InputExample

10
540 648 810 648 720 540 594 864 972 648
OutPutExample
54

题面分析

给你 n n n个数,让你求出两两组合的LCM的GCD。
先放个作者的题解。
注意到 n n n的数字很大,所以暴力一定T飞,因此考虑别的方法。
我想到了质因子分解
由唯一分解定理可知,一个数可以被分解为
p 1 k 1 p 2 k 2 p 3 k 3 . . . p m k m p_1^{k_1}p_2^{k_2}p_3^{k_3}...p_m^{k_m} p1k1p2k2p3k3...pmkm的形式。
LCM可以认为是在每个 k i k_i ki之中取较大值,
GCD可以认为是在每个 k i k_i ki之中取较小值。
于是我们可以直接考虑将 n n n个数质因子分解,对每个质因子进行处理。
于是,问题就变成如下。
已知数列 k i k_i ki,求出 M i n ( M a x ( k i , k j ) ) ( i ≤ j ) Min(Max(k_i,k_j))(i\leq j) Min(Max(ki,kj))(ij)
我们不妨设 k i ≤ k j k_i\leq k_j kikj,可以发现, M i n ( M a x ( k i , k j ) ) ( i ≤ j ) Min(Max(k_i,k_j))(i\leq j) Min(Max(ki,kj))(ij)就是 k 2 k_2 k2
因为两两取最大值中的最小值,一定是 M a x ( k 1 , k 2 ) = k 2 Max(k_1,k_2)=k_2 Max(k1,k2)=k2是最小的。
于是我们的步骤如下:
1.对n个数质因子分解。
2.求出每个质因子系数里第二小的。
3.合并求出答案。

这里可以注意几个细节。
如果满足某个 k i = 0 k_i=0 ki=0的数字出现了两次及以上,那 k 2 = 0 k_2=0 k2=0,不予考虑。
如果满足某个 k i = 0 k_i=0 ki=0的数字出现了一次,那 k 2 k_2 k2为最小的不为零的系数。
如果满足某个 k i = 0 k_i=0 ki=0的数字没有出现,那 k 2 k_2 k2就是第二小的不为零的系数。

AC代码(311ms)

#include<bits/stdc++.h>
using namespace std;
long long a[2000005];//a是原数组
long longfactor[2000005];//factor[i]是质因子i出现的次数
vector<int> k[2000005];///k[i][j]数组表示质因子为i的第j个的个数
long quick_pow(long long a,long long b)//二进制快速乘,合并质因子与其系数时可用
{
	long long ans=1;
	long long base=a;
	while(b!=0)
	{
		if(b&1)
			ans=ans*base;
		base=base*base;
		b>>=1;
	}
	return ans;
}
int main()
{
	long long n;
	scanf("%lld",&n);
    for(int i=1;i<=n;i++)
	{
        scanf("%lld",&a[i]);
        long long x=a[i];
        for(int j=2;j*j<=x;j++)
		{
            if(x%j==0)
			{
                factor[j]++;
                int count=0;
                while(x%j==0)
                {
					x/=j;
					count++;
                }
                k[j].push_back(count);
            }
        }
        if(x!=1)
		{
            factor[x]++;
            k[x].push_back(1);//可以证明,对于任意一个数x,最多会有一个大于sqrt(x)的质因子 
        }
    }
    for(int i=1;i<=200000;i++) 
		sort(k[i].begin(),k[i].end());
    long long answer=1;
    for(int i=1;i<=200000;i++)
	{
		if(factor[i]==n-1)
			answer*=quick_pow(i,k[i][0]);//处理ki=0只出现一次的情况
        else if(factor[i]==n)
			answer*=quick_pow(i,k[i][1]);//处理ki=0没有出现的情况
    }
    printf("%lld",answer);
} 

后记

其实这个代码可以改进的地方可以很多,可以实现筛出 2 e 5 2e5 2e5以内的质数再去做操作之类的…
居然错过了这个Mathy的div2场,痛失回到1500的机会 (大误)
DrGilbert 2020.5.13

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值