Codeforces Round #641 (Div. 2) C
数论
很有意思的一道题,有一说一
–>Link
C. Orac and LCM
-
题意:给你一个长为n的数串,它们之间两两取lcm得到一个新的数串,再取总gcd,求所得答案
-
思路:(我太菜了,我觉得很难,但是的确很有意思,参考了一些大佬的解析,我决定自己写一个)
- 基础补充:
对于任意一个数,可以表示成为: x = p 1 a 1 ∗ p 2 a 2 ∗ . . . ∗ p n a n ; x=p_1^{a_1}*p_2^{a_2}*...*p_n^{a_n}; x=p1a1∗p2a2∗...∗pnan; y = p 1 b 1 ∗ p 2 b 2 ∗ . . . ∗ p n b n ; y=p_1^{b_1}*p_2^{b_2}*...*p_n^{b_n}; y=p1b1∗p2b2∗...∗pnbn;
而对于gcd,lcm: l c m ( x , y ) = p 1 m a x ( a 1 , b 1 ) ∗ p 2 m a x ( a 2 , b 2 ) ∗ . . . ∗ p n m a x ( a n , b n ) lcm(x , y)=p_1^{max(a_1,b_1)}*p_2^{max(a_2,b_2)}*...*p_n^{max(a_n,b_n)} lcm(x,y)=p1max(a1,b1)∗p2max(a2,b2)∗...∗pnmax(an,bn) g c d ( x , y ) = p 1 m i n ( a 1 , b 1 ) ∗ p 2 m i n ( a 2 , b 2 ) ∗ . . . ∗ p n m a x ( a n , b n ) gcd(x , y)=p_1^{min(a_1,b_1)}*p_2^{min(a_2,b_2)}*...*p_n^{max(a_n,b_n)} gcd(x,y)=p1min(a1,b1)∗p2min(a2,b2)∗...∗pnmax(an,bn)
究其本质后,就好做了。
- 基础补充:
-
解题:那么,开始求lcm的时候,就是两两之间求幂次的最大,再求gcd的时候,就是所有数(lcm过后)中每个质数幂次的最小。(不理解的话其实可以随便举两个数,去套入上面的gcd、lcm公式去算一两遍就好多了)
对于任意一个质因子: p i p_i pi化成单个来理解,它的幂次(lcm之前)分别为: a i , b i , c i . . . ( 不 排 除 0 ) a_i,b_i,c_i...(不排除0) ai,bi,ci...(不排除0)在其中两两求取最大值,再在其中求最小值,就是该质数对答案的贡献了。
现在就是要从原始数组里直接找到最后的最小值。那我们开始考虑,对于这个质因子(设为x)来说:
-
如果原始数列所有数都包含了该质因子:
那么对于这个因子,它在所有数上的幂次都大于0(因为都出现了),两两求取幂次的最大值后,再求其中的最小值,其中最小值即为:pri[ x ][ 1 ]
则ans*=qpow( x , pri[x][1])pri意为质数x在所有数中出现的幂次,1为第二小的幂次
为什么是第二小?
在x出现的所有幂次中,两两取最大值后,其中的最小值即为原数组最小,和第二小的两数取max,取得的值为第二小的值。
-
若该质因子仅被包含n-1次,那么有一个数会不包含因子x。相当于在这里幂数取了0。那么在两两取最大值后,其中的最小值即为原数组最小,和这个0的两数的max,取得的值为最小值
则ans*=qpow( x ,pri[ x ][ 0 ]) -
若小于n-1次,同理,至少会有两个数包含x的幂次是0,那么最小值是0,对答案无贡献,不考虑这个情况。
- 综上:
对每个数进行质因分解,并且求的每个质数的幂次。
对每个质数的幂次排序
对于每个质数,数它在数列中出现的次数- 若为n次,它对答案的贡献是:x的倒数第二大幂次次方
- 若为n-1次,它对答案的贡献是:x的最小幂次次方
- 否则,不考虑
DONE
#include<cassert>
#include<string>
#include<cmath>
#include<cstring>
#include<stack>
#include<iostream>
#include<map>
#include<set>
#include<algorithm>
#include<vector>
#include<cstdlib>
#pragma warning(disable:4996)
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f
#define itn int
#define ll long long
#define mes(a,k) memset(a,k,sizeof(a))
//#define max(a,b) a>b?a:b
//#define min(a,b) a<b?a:b
#define eps 1e-8
//#define pb(a) push_back(a)
using namespace std;
const int N = 2e5 + 5;
using namespace std;
vector<int> pri[N];//pri[i][j]表示:质数i在各个数中出现的幂数,j仅作为下标
int cnt[N];//质数i在所有的数中出现的次数(可用pri[i].size()代替)
void io() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); }
ll qpow(ll a, ll b) {//快速幂捞一下
ll ans = 1;
while (b) {
if (b & 1) {
ans *= a;
}
a *= a;
b >>= 1;
}
return ans;
}
int main() {
io();
int T;
cin >> T;
for (int i = 0; i < T; ++i) {
int x;
cin >> x;
for (int j = 2; j * j <= x; ++j) {//找出质因子
if (x % j == 0) {
cnt[j]++;
int po = 0;
while (x % j == 0) {
x /= j;
po++;
}
pri[j].push_back(po);
}
}
if (x != 1) {
cnt[x]++;//我是我自己
pri[x].push_back(1);
}
}
ll ans = 1;
for (int i = 1; i <= N-3; i++) {
if (cnt[i] >= T - 1) {
sort(pri[i].begin(), pri[i].end());
if (cnt[i] == T - 1)
ans *= qpow(i,pri[i][0]);//若是T-1,则取幂数最小
else
ans *= qpow(i,pri[i][1]);//是T(全勤),则取幂数第二小
}
}
cout << ans << endl;
return 0;
}