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;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章