【CCF CSP-J 2023】一元二次方程
Part -1:遗憾总结
这一题作为 CSP-J 第三题还有有点实力的。
当时在考场只剩一点时间 + 我看见
2
2
2 页 pdf 直接愣住了,题都懒得读了直接特殊性质 A+B 10pts,现在想来那是九分甚至十分的后悔啊…应该再好好想想。不然能骗60pts
Part 1: 整数分数
标注一下: x : a x:a x:a, y : b y:b y:b, z : Δ z:{\sqrt {\Delta}} z:Δ
思路:
首先,我们把这些都视为分数进行读入。
其次呢,当 x < 0 x < 0 x<0 的时候,那么这个式子肯定等于负数,但是分数你又不能把负号放在分母上(读样例得知),那就放在分子上,同时 x x x 也要变成正数所以 y = − y y=-y y=−y, x = − x x=-x x=−x。
然后就开始操作:
拿两个变量去记录分子分母,由于求的是最大值,所以只考虑加法。
再去拿一个变量去记录分子分母的最大公因数,由于可能是负数,所以要加上 abs。
最后进行约分,再判断分母是否为 1 1 1,如果是,就按整数输出,否则按照分数输出。
这一部分的代码:
inline void print1(int x,int y,int z){
if(x<0)x=-x,y=-y;
int fz=-y+z,fm=2*x;
int gcd=abs(__gcd(fz,fm));
fz/=gcd,fm/=gcd;
if(fm==1)cout<<fz;
else cout<<fz<<'/'<<fm;
}
Part 2:根号
考虑完了整数分数,我们就来考虑一下根号的情况。
标注一下变量初始值: d t : Δ dt:\Delta dt:Δ, k : Δ k:\sqrt \Delta k:Δ。
思路:
首先,前面有一个分数的部分,只要 b ≠ 0 b \ne 0 b=0 就去执行这一部分。由于只有这一部分, Δ \sqrt{\Delta} Δ 传入 0 0 0 即可。分数部分就解决完了。
随后,我们读题,不难发现,他的根号表达方式是写成了一种对于我这种小学生来说比较新颖的方式。
举个例子: 45 \sqrt {45} 45 在样例上写作的是 3 ⋅ 5 3\cdot \sqrt{5} 3⋅5。
因为 45 45 45 可以拆分成 3 2 × 5 3^2\times5 32×5, 3 2 = ± 3 \sqrt{3^2} = \pm3 32=±3,可以直接拿出来,由于取最大值,所以选择 + 3 +3 +3。
这个我们要如何去获取呢?其实也很简单:我们把 Δ \Delta Δ 开方,得到一个近似值,一直往下寻找,直到发现 Δ % i 2 = 0 \Delta ~\% ~i^2 =0 Δ % i2=0 由于是从上往下寻找,所以不用担心有剩余这个问题。将 Δ ÷ i 2 \Delta \div i^2 Δ÷i2 的值存进 t t t 里待会输出的时候用。
输出的时候,我们效仿整数分数那一段,当 a < 0 a<0 a<0 的时候,由于前面已经处理了 − b ÷ 2 a -b \div 2a −b÷2a,就只需要把 a a a 变成 − a -a −a 就可以了。然后第三、四行我不解释,前面讲过了。主要是第二行。后面的 r r r 在输出时弄就行了,这里不需要进行处理(无理数)。分子 k k k 现在变成了 Δ ÷ r \Delta \div r Δ÷r,也就是 i 2 i^2 i2,在这里作为分子看看能不能约分。分母这个就不用解释了。
在后面的正式输出,如果:
-
k ′ k^{'} k′、分母都等于一:那么式子就只有 r \sqrt{r} r。
-
分母等于一的时候,式子就只有 k ′ ⋅ r k^{'} \cdot \sqrt r k′⋅r。
-
k ′ k^{'} k′ 等于一的时候,式子剩下 r 2 a \frac{\sqrt r }{2a} 2ar。
-
这些情况都不满足的时候,式子就是 k ′ ⋅ r 2 a \frac{k^{'} \cdot \sqrt r}{2a} 2ak′⋅r
- 注: k ′ k^{'} k′ 为 k k k 约分后的结果
上面这些都是输出格式,照着这样输出就行了。
这一部分的代码:
inline void init1(int a,int k,int r){
if(a<0)a=-a;
int fz=k,fm=2*a;
int gcd=abs(__gcd(fz,fm));
fz/=gcd,fm/=gcd;
if(fm==1&&fz==1){
cout<<"sqrt("<<r<<")";
}else if(fm==1){
cout<<fz<<"*sqrt("<<r<<")";
}else if(fz==1){
cout<<"sqrt("<<r<<")/"<<fm;
}else{
cout<<fz<<"*sqrt("<<r<<")/"<<fm;
}
}
inline void print2(int a,int b,int dt,int k){
// (-b + sqrt(dt)) / 2a
if(b!=0)print1(a,b,0),cout<<'+';
while(dt%(k*k))--k;
int r=dt/(k*k);
init1(a,k,r);
}
Part 3:结合
最后组合一下,写一下主函数,记得
Δ
<
0
\Delta < 0
Δ<0 的时候要输出 NO
,最终代码就出来啦。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t;
int* eat = new int;
inline void print1(int x,int y,int z){
if(x<0)x=-x,y=-y;//把负号传到分子部分
int fz=-y+z,fm=2*x;//计算分子分母
int gcd=abs(__gcd(fz,fm));//最大公因数
fz/=gcd,fm/=gcd;//约分
if(fm==1)cout<<fz;//整数
else cout<<fz<<'/'<<fm;//分数
}
inline void init1(int a,int k,int r){
if(a<0)a=-a;//把负号传到分子部分
int fz=k,fm=2*a;//计算分子分母
int gcd=abs(__gcd(fz,fm));//最大公因数
fz/=gcd,fm/=gcd;//约分
if(fm==1&&fz==1){//上面说的四种情况
cout<<"sqrt("<<r<<")";
}else if(fm==1){
cout<<fz<<"*sqrt("<<r<<")";
}else if(fz==1){
cout<<"sqrt("<<r<<")/"<<fm;
}else{
cout<<fz<<"*sqrt("<<r<<")/"<<fm;
}
}
inline void print2(int a,int b,int dt,int k){
// -b + sqrt(dt) / 2a
if(b!=0)print1(a,b,0),cout<<'+';//前面分数的部分
while(dt%(k*k))--k;//上面说的“一种新颖的输出方式”
int r=dt/(k*k);
init1(a,k,r);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin>>t>>*eat;//我个人认为第二个没有用,就直接把它吃掉了
delete eat;
while(t--){
int a,b,c;
cin>>a>>b>>c;
int delta=b*b-4*a*c;//计算出 Delta
int k=sqrt(delta);//这里会自动向下取整
if(delta < 0){//如果Delta小于0,则无实数解
cout<<"NO";
}
else if(k*k == delta){//整数分数
print1(a,b,k);
}
else{//根号
print2(a,b,delta,k);
}
cout<<'\n';//记得换行
}
}
看我写了这么多,给个赞不过分吧?