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} , define the Greatest Common Divisor (GCD) and Least Common Multiple (LCM) of s as follow:
g c d ( s ) gcd(s) is the maximum positive integer x x , such that all integers in s s are divisible on x x .
l c m ( s ) lcm(s) is the minimum positive integer x x , that divisible on all integers from 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 and l c m ( 4 , 6 ) = 12 lcm({4,6})=12 . Note that for any positive integer x x , g c d ( { x } ) = l c m ( { x } ) = x gcd(\{x\})=lcm(\{x\})=x .
Orac has a sequence a a with length 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\} , and asked you to find the value of g c d ( 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 ( 2 n 1 e 5 ) (2≤n≤1e5) .
The second line contains n n integers, a 1 , a 2 , , a n a_1,a_2,…,a_n ( 1 a i 2 e 5 ) (1≤a_i≤2e5) .
Output
Print one integer: g c d ( { l c m ( a i , a j ) i < j } ) gcd(\{lcm({a_i,a_j}) | i<j\}) .
InputExample

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

题面分析

给你 n n 个数,让你求出两两组合的LCM的GCD。
先放个作者的题解。
注意到 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} 的形式。
LCM可以认为是在每个 k i k_i 之中取较大值,
GCD可以认为是在每个 k i k_i 之中取较小值。
于是我们可以直接考虑将 n n 个数质因子分解,对每个质因子进行处理。
于是,问题就变成如下。
已知数列 k i k_i ,求出 M i n ( M a x ( k i , k j ) ) ( i j ) Min(Max(k_i,k_j))(i\leq j)
我们不妨设 k i k j k_i\leq k_j ,可以发现, M i n ( M a x ( k i , k j ) ) ( i j ) Min(Max(k_i,k_j))(i\leq j) 就是 k 2 k_2
因为两两取最大值中的最小值,一定是 M a x ( k 1 , k 2 ) = k 2 Max(k_1,k_2)=k_2 是最小的。
于是我们的步骤如下:
1.对n个数质因子分解。
2.求出每个质因子系数里第二小的。
3.合并求出答案。

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

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 以内的质数再去做操作之类的…
居然错过了这个Mathy的div2场,痛失回到1500的机会 (大误)
DrGilbert 2020.5.13

猜你喜欢

转载自blog.csdn.net/oampamp1/article/details/106108361