欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Orac and LCM(GCD/LCM/质因子分解/推导证明)Codeforces Round #641 (Div. 2) C题

程序员文章站 2022-06-03 19:38:34
...

Orac and LCM(GCD/LCM/质因子分解/推导证明)Codeforces Round #641 (Div. 2) C题

题目大意

       给n(n100000)n(n \le 100000)个数ai(ai200000)a_i(a_i \le 200000),求gcd({lcm({ai,aj})i<j})gcd(\{lcm(\{a_i,a_j\})|i<j\}).

分析过程

       考虑每个数的素因子分解,对于(ai,aj)(a_i,a_j)的所有组合,lcm(ai,aj)lcm(a_i,a_j)对应的素因子pkp^k总满足k=max(ki,kj)k=max(k_i,k_j),即取二者最大的那一个,而对于gcdgcd来说则是取minmin。于是,对于结果ansans来说,一定有一个唯一的素数分解,其中每一个素数pkp^kkk等于所有lcm(ai,aj)lcm(a_i,a_j)组合中pp的最小值,而对于所有lcm(ai,aj)lcm(a_i,a_j)来说,能够得到的最小值是所有aia_ipkp^k的次小值kk,因为最小的那个kk肯定会由于lcmlcmmaxmax特性被掩盖掉,而当最小的kk和次小的kk对应的aia_i在一起求lcmlcm时,次小的那个kk对应的aia_i会被保留下来。
       所以,这道题先用类似于埃拉斯托特尼筛选法的方式预处理标记一遍aia_i的最小素因子,然后对于每个aia_i进行素因子分解,然后维护对应素因子的最小值和次小值即可。

Another Solution

       还有一种做法,利用公式gcd(lcm(a1,a2),lcm(a1,a3)...lcm(a1,an))=lcm(a1,gcd(a2,a3,...an))gcd( lcm(a1 , a2) , lcm(a1 , a3) ... lcm(a1 , an) ) =lcm (a1 , gcd (a2 , a3 , ... an) )
这样只需要求前缀和后缀gcdgcd就行了。
关于这个公式的证明留个坑,等之后来填~!

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 100;
const int N = 200000;
ll flag[N+5], a[maxn], cnt[N+5][3], n;
void preTreat(){
	int i, j;
	for(i=2;i<=N;++i){ //预处理标记每个数能够被整除的最小素因子 
		if(flag[i]) continue;
		for(j=i;j<=N;j+=i){
			if(!flag[j]) flag[j] = i;
		}
	}
	for(i=1;i<=N;++i) cnt[i][0] = cnt[i][1] = N;
}
void solve(){
	int i;
	ll ans = 1;
	for(i=1;i<=n;++i){
		while(a[i] > 1){
			ll c = 0, temp = a[i]; 
			while(a[i] % flag[temp] == 0){
				a[i] /= flag[temp];
				++c;	
			}
			if(c < cnt[flag[temp]][0]) swap(cnt[flag[temp]][0], c);
			if(c < cnt[flag[temp]][1]) swap(cnt[flag[temp]][1], c);
			++cnt[flag[temp]][2]; //记录此因子存在于多少个树中 
		} 
	}
	for(i=2;i<=N;++i){
		if(cnt[i][2] < n - 1) continue;
		ll j = cnt[i][1];
		if(cnt[i][2] == n - 1) j = cnt[i][0];
		while(j--) ans *= i; 
	}
	cout<<ans;
} 
int main(){
	int i;
	ios::sync_with_stdio(false);
	cin>>n;
	for(i=1;i<=n;++i) cin>>a[i];
	preTreat();
	solve();
	return 0;
}