P8584 探索未知 题解
题意
给你 \(n\) 个分数,每个分数后面跟着一个操作符 \(op\) , 如果为 \(1\) 就是加上这个分数,是 \(2\) 就减去。初始时是 \(0\) , 询问 \(n\) 次操作后最后的分数是多少,化成最简分数。
特殊地,如果最后是个整数,直接以整数的形式输出。
思路
模拟
考试的时候一看就想到了 [NOIP2020]排水系统,只不过这个题很良心,保证计算的时候不会出现 $2 \times 10^9 $ 以上的数,所以可以不用写高精或者是用 __int128
。
-
首先我们要有最基础的数论知识:
用 \(\gcd(a,b)\) 表示 \(a\) 和 \(b\) 的最大公约数,用 \(\text{lcm}(a,b)\) 表示 \(a\) 和 \(b\) 的最小公倍数。
性质一:\(\gcd(a,b) = \gcd(b,a\% b)\) ,有了这个性质,我们就可以快速求出 \(a\) 和 \(b\) 的最大公约数,至于它的证明,我觉得这篇博客写的就很不错。
性质二:\(\text{lcm}(a,b) = a \times b / \gcd(a,b)\)
证明:由唯一分解定理可得
我们设 \(a = p_1^{a_1} \times p_2^{a_2}\times \dots \times p_m^{a_m}\),
\(b = p_1^{b_1} \times p_2^{b_2}\times \dots \times p_m^{b_m}(a_i,b_i \ge 0)\)。
\(\therefore\) \(a \times b = p_1^{a_1+b_1}p_2^{a_2+b_2}\dots p_m^{a_m+b_m}\)
$ = p_1^{\min(a_1,b_1)+\max(a_1,b_1)}\dots p_m^{\min(a_m,b_m)+\max(a_m,b_m)}$
$ = \gcd(a,b) \times \text{lcm}(a,b)$
证毕。
有了这两个性质,我们就可以很好的求出两个数的最大公约数和最小公倍数了。
-
接下来,模拟即可。可以通过代码来理解。
代码实现
#include<bits/stdc++.h>
#define int long long
#define ll long long
#define next nxt
#define re register
#define il inline
using namespace std;
int max(int x,int y){return x > y ? x : y;}
int min(int x,int y){return x < y ? x : y;}
int n,fz,fm,nfz,nfm;
int op;
il int read()
{
int f=0,s=0;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) f |= (ch=='-');
for(; isdigit(ch);ch=getchar()) s = (s<<1) + (s<<3) + (ch^48);
return f ? -s : s;
}
il int gcd(int a,int b) { return b ? gcd(b,a%b) : a; }
il int lcm(int a,int b) { return a*b/gcd(a,b); }
il int Abs(int x) { return x < 0 ? -x : x; }
signed main()
{
n = read() - 1;
fz = read() , fm = read() , op = read();/*首先录入第一个分数*/
if(op == 2) fz = -fz;/*是减法的话,让分子变为负数*/
for(re int i=1;i<=n;i++)
{
nfz = read() , nfm = read() , op = read();/*输入新分子和分母*/
if(fz == 0) { if(op == 2) fz = -nfz; else fz = nfz ; fm = nfm; continue; }/*如果原先的数是0,那么直接重新赋值即可*/
int Lcm = lcm(fm,nfm);/*找到分母的最小公倍数*/
//cout << Lcm << endl;
fz *= (Lcm/fm) , nfz *= (Lcm/nfm);/*分子乘上分母变成最小公倍数的倍数*/
//cout << fz << " " << nfz << " ";
if(op == 1) fz+=nfz; else fz-=nfz;/*分母相等,可以对分子做加减运算*/
fm = Lcm;/*分母变成最小公倍数*/
int Gcd = \gcd(Abs(fz),fm);/*给分数约分,注意分子可能为负数,所以要用绝对值找最大公因数*/
fz /= Gcd; fm /= Gcd;
}
if(fz%fm == 0)/*如果是整数*/
{
if(fz < 0)
printf("-%lld",Abs(fz)/\gcd(Abs(fz),fm));/*小于0加负号*/
if(fz == 0) printf("0");/*分子是0输出0*/
if(fz > 0) /*大于不用负号*/
printf("%lld",fz/\gcd(fz,fm));
}
else printf("%lld/%lld",fz,fm);/*分数直接输出*/
return 0;
}
时间复杂度 \(\Theta(n\log n)\) ,可以通过。
自认为写的很详细了,有什么问题可以在评论区问。