题目链接:https://codeforces.com/problemset/problem/222/C
题目大意:
两个数组a,b分别长为n, m。有这么个分数,分子是a数组的乘积,分母是b数组的乘积,现在要你找出两个与a和b等价的数组,使得分数大小不变,而构造出来的分数是最简的。数组长度最大为1e5,元素最大为1e7。
解题思路:
容易想到的是把a,b数组每一个数经行质因数分解,约掉重复的质因数,根据剩下的质因数对原数组的每一个数进行约分。但是,无论是分解质因数还是约分,若是用一般的思路,在此数据范围下,必然超时。所以要在循环条件下手,加一点优化。具体看代码解释。
代码如下:
# include <bits/stdc++.h>
using namespace std;
const int maxn = 1e7 + 5;
bool prime[maxn];
vector <int> p;
int pa[maxn], pb[maxn];
int a[100005], b[100005];
int n, m;
void init(){
for(int i = 2; i < maxn; ++i){
if(!prime[i]){
p.push_back(i); //把质数放入一个vector中,分解数时要用上
for(int j = i+i; j < maxn; j += i)
prime[j] = true;
}
}
}
void div(int x, int a[]){
for(int i = 0; i < p.size() && prime[x]; ++i){ //如果是质数及时跳出
while(x % p[i] == 0){
a[p[i]] ++;
x /= p[i];
}
}
if(x > 1) a[x]++;
return ;
}
int reduce(int x, int a[]){
int res = 1;
int fac;
for(int i = 0; i < p.size() && prime[x]; ++i){ //如果是质数及时跳出
fac = p[i];
while(x % fac == 0){
if(a[fac]){
a[fac]--;
res *= fac;
}
x /= fac; //约掉多余的
}
}
if(x > 1 && a[x] > 0){
a[x]--;
res *= x;
}
return res;
}
int main(){
std::ios::sync_with_stdio(false);
init();
while(cin >> n >> m){
for(int i = 0; i < n; ++i){
cin >> a[i];
div(a[i], pa);
}
for(int i = 0; i < m; ++i){
cin >> b[i];
div(b[i], pb);
}
int extra, fac;
for(int i = 0; i < p.size(); ++i){ //约掉多余的
fac = p[i];
extra = min(pa[fac], pb[fac]);
pa[fac] -= extra;
pb[fac] -= extra;
}
cout << n << ' ' << m << endl;
for(int i = 0; i < n; ++i) //给每一个数约分
cout << reduce(a[i], pa) << ' ';
cout << endl;
for(int i = 0; i < m; ++i)
cout << reduce(b[i], pb) << ' ';
cout << endl;
memset(pa, 0, sizeof(pa));
memset(pb, 0, sizeof(pb));
}
return 0;
}
因为在分解时遍历的是存放素数的vector,进行循环的条件是x要是素数,那么,依次循环最多遍历(1~1e3.5)中的素数,根据素数定理可知,总复杂度小于(1e8)。