T3
分析
简单分析一下,该题本质上就是让求能将
n
n
n个分数全部以该进制下的有限小数形式表示出来的进制数,即对于题中所给的
n
n
n个十进制下的分数,让求最小的能将这
n
n
n个分数全部以有限小数形式表示出来的进制数。
对于此题,我们可以换个角度考虑,即哪些分数可以用有限位数小数表示。首先,我们从十进制开始考虑1。
很容易想到,分数中决定是否能用有限位小数表示的只有它的分母2。所以,我们只用考虑所有能用有限位小数表示的分数的分母的特点就行了。
对于一个·
p
p
p进制小数,我们可以将其的十进制形式表示为
a
n
×
p
n
+
a
n
−
1
×
p
n
−
1
+
.
.
.
+
a
2
×
p
2
+
a
1
×
p
1
+
a
0
×
p
0
+
a
−
1
×
p
−
1
+
a
−
2
×
p
−
2
+
.
.
.
+
a
−
(
m
−
1
)
×
p
−
(
m
−
1
)
+
a
−
m
×
p
−
m
a_n\times p^n+a_{n - 1}\times p^n - 1+ ... +a_2\times p^2+a_1\times p^1 +a_0\times p^0 +a_{_{-1}} \times p ^{-1} +a_{_{-2}}\times p ^{-2} +... + a_{_{-(m - 1)}}\times p^{-(m - 1)} + a_{_{-m}}\times p^{-m}
an×pn+an−1×pn−1+...+a2×p2+a1×p1+a0×p0+a−1×p−1+a−2×p−2+...+a−(m−1)×p−(m−1)+a−m×p−m
因此,对于任意一个
n
n
n位的
p
p
p进制有限小数,该小数均可以表示为
p
p
p的倍数为分母的一个分数,同理可得对于任意一个分母为
p
p
p的十进制分数,均可以用一个进制数为
p
p
p的倍数的有限小数来表示。回归题目,可以猜想当进制数为所有给定分数的最简分数形式的分母的公倍数时,所有的已知分数可以用有限位小数表示。
对于上述结论,依据算术基本定理,我们可以进一步得出对于分母的所有质因子的乘积
p
r
pr
pr,
p
r
pr
pr进制数也可以用有限位小数表示该分数3。而且对于整数进制数而言,该进制数一定是最小的。所以答案就是所有分数的最简分数形式的所有不同质因子的乘积。
实现
该题的难点一部分在做法上,另一部分在实现方法上。首先,题目要求以 16 16 16进制数输出答案,所以进制转换是必不可少的。另一方面,该题数据量较大,需要考虑优化算法复杂度和算法精度。经检验,该题部分数据已无法用 l o n g long long l o n g long long表示,所以高精度算法是必须的。
code
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include <set>
#define LL long long
using namespace std;
int top;
int f[100000000] , P[10000000] ;
long long num[10000000] , ans[1000000];//高精乘
char ch[100000000];//十六进制的答案
int gcd(int x , int y)//最大公约数(约分用)
{
if(x > y)
swap(x , y);
return y % x ? gcd(y % x , x) : x;
}
void Prime(int n)//线性筛
{
for(int i = 2 ; i <= n ; i++)
{
if(!f[i])
{
f[i] = i;
P[top++] = i;
}
for(int j = 0 ; j < top && P[j] < n / i && P[j] <= f[i] ; j ++)
{
f[P[j] * i] = P[j];
}
}
}
void mul(int n)//高精度乘法(十六进制版)
{
int len = strlen(ch);
for(int i = len - 1 ; i >= 0 ; i --)//将字符串转换为数字数组以便计算
{
if('0' <= ch[i] && ch[i] <= '9')
num[len - i - 1] = ch[i] - '0';
else
num[len - i - 1] = ch[i] - 'A' + 10;
}
for(int i = 0 ; i < len ; i ++)
{
ans[i] += num[i] * 1ll * n;
num[i] = 0;
while(ans[i] > 15)
{
if(i == len - 1)
len++;
ans[i + 1] += ans[i] / ( 1ll * 16 );
ans[i] %= 1ll * 16;
}
}
for(int i = len - 1 ; i >= 0 ; i --)//将数字数组转换回字符串以便输出
{
if(ans[i] < 10)
ch[len - i - 1] = ans[i] + '0';
else
ch[len - i - 1] = (ans[i] - 10) + 'A';
ans[i] = 0;
}
ch[len] = '\0';
}
int main()
{
int n;
scanf("%d" , &n);
set<int> prime;//去重用
Prime(1000000);
for(int i = 0 ; i < n ; i ++)
{
int x , y;
scanf("%d%d" , &x , &y);
int temp = gcd(x , y);
x /= temp;
y /= temp;//约分
int e = sqrt(y);
for(int i = 0 ; P[i] <= e ; i ++)//质因子分解(预处理优化)
{
if(!(y % P[i]))
{
prime.insert(P[i]);
while(!(y % P[i]))
{
y /= P[i];
}
}
}
if(y > 1)
{
prime.insert(y);
}
}
long long sum = 1;
set<int>::iterator it;//迭代器
ch[0] = '1';
ch[1] = '\0';
for(it=prime.begin(); it!=prime.end(); ++it)//计算所有质因子的乘积
{
int n = *it;
mul(n);
}
printf("%s" , ch);
return 0;
}