洛谷P1042题目链接https://www.luogu.org/problemnew/show/P1042

本文介绍了一个简单的比赛得分统计程序,该程序使用C++编写,能够根据输入的比赛过程字符流(如'W'代表赢,'L'代表输),统计并输出每场胜利的比分,分别针对两种不同的胜利条件(11分制与21分制)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include <iostream>  
#include <stdio.h>  
#include <string.h>   
#include <stack>  
#include <queue>   
#include <map>  
#include <set>  
#include <vector>  
#include <math.h>  
#include <bitset>  
#include <algorithm>  
#include <climits>   
using namespace std;  
#define lson 2*i  
#define rson 2*i+1 
#define Pair pair<int, int> 
#define ULL unsigned long long
#define LS l,mid,lson  
#define RS mid+1,r,rson  
#define up(i,x,y) for(int i=x;i<y;i++)  
#define down(i,x,y) for(int i=x;i>=y;i--)  
#define MEM(a,x) memset(a,x,sizeof(a))  
#define W(a) while(a)  
#define gcd(a,b) __gcd(a,b)  
#define LL long long  
#define N 150005  
#define MOD 1000000007  
#define INF 0x3f3f3f3f  
#define EXP 1e-8  
#define lowbit(x) (x&-x)  
#define _  ios_base::sync_with_stdio(0),cin.tie(0)
//freopen("1.txt", "r", stdin);
#include <iostream>  
#include <stdio.h>  
#include <string.h>   
#include <stack>  
#include <queue>   
#include <map>  
#include <set>  
#include <vector>  
#include <math.h>  
#include <bitset>  
#include <algorithm>  
#include <climits>   
using namespace std;  
#define lson 2*i  
#define rson 2*i+1 
#define Pair pair<int, int> 
#define ULL unsigned long long
#define LS l,mid,lson  
#define RS mid+1,r,rson  
#define up(i,x,y) for(int i=x;i<y;i++)  
#define down(i,x,y) for(int i=x;i>=y;i--)  
#define MEM(a,x) memset(a,x,sizeof(a))  
#define W(a) while(a)  
#define gcd(a,b) __gcd(a,b)  
#define LL long long  
#define N 150005  
#define MOD 1000000007  
#define INF 0x3f3f3f3f  
#define EXP 1e-8  
#define lowbit(x) (x&-x)  
#define _  ios_base::sync_with_stdio(0),cin.tie(0)
//freopen("1.txt", "r", stdin);
string s;
int main(){
    int a=0,b=0,c=0;
    getline(cin,s,'E');//获取s,直到遇到'E'
    for(int i=0;i<s.size();i++){
        //if
        if(s[i]=='W')
        a++;
        if(s[i]=='L')
        b++;
        if(s[i]=='E')
        break;
        if((a>=11||b>=11)&&abs(a-b)>=2){//如果if放到for下面第一句开头会90分,可能出现11进制11:0  0:0而21进制就进行一场22:0,大家可以想一下
            cout<<a<<":"<<b<<endl;
            a=0;
            b=0;
        }
        
    
}
    cout<<a<<":"<<b<<endl<<endl;
    a=0,b=0;
    for(int i=0;i<s.size();i++){
        if(s[i]=='W')
        a++;
        if(s[i]=='L')
        b++;
        if(s[i]=='E')
        break;
        if((a>=21||b>=21)&&abs(a-b)>=2){
            cout<<a<<":"<<b<<endl;
            a=0;
            b=0;
        }
        
    
}
    cout<<a<<":"<<b<<endl;
}

 

<think>根据题目链接(P9750 [CSP-J 2023] 一元二次方程)的要求,我们需要输出一元二次方程的解,并且分母不需要合并(即按照题目要求的格式输出)。原代码存在变量名冲突等问题,需要修正。 主要问题: 1. 变量名冲突:在循环内部重新定义了变量t(判别式),覆盖了外层的t(测试用例数量)。这会导致循环次数错误。 2. 输出格式问题:题目要求输出最简形式,包括有理部分和无理部分,且分母不需要合并(即可以分别输出有理部分和无理部分,中间用加号连接,但注意当有理部分为0时,不输出有理部分;无理部分系数为1时不输出系数等)。 3. 符号处理问题:在约分时,对符号的处理不够完善。 修正思路: 1. 将判别式的变量名改为其他名称(如delta)以避免冲突。 2. 按照题目要求,将解分为两部分:有理部分(即-b/(2a))和无理部分(即sqrt(delta)/(2a))。分别化简这两部分。 3. 注意约分时符号的处理:分子分母同除以最大公约数(绝对值),然后根据符号规则调整(分子分母同号为正,异号为负)。 4. 当判别式为完全平方数时,只输出有理部分(将有理部分和无理部分合并)。 5. 输出时,如果两部分都存在,则用加号连接(注意有理部分为负数时,负号会显示在有理部分,此时无理部分用正号,但通常我们省略正号,不过题目要求最简,所以当有理部分为负时,无理部分前不需要加号,但代码中已经处理了有理部分非负时加号的情况)。 具体修正步骤: 1. 将内层变量t改为delta。 2. 在计算delta后,如果delta<0,输出"NO"。 3. 提取delta的平方因子(x),使得delta = x^2 * delta',其中delta'是无平方因子的。 4. 有理部分:分子为-b(注意已经将b赋值为-b,所以后续直接使用b),分母为2a(即a*2,所以a已经变为2a)。 5. 约分有理部分:计算b和a的最大公约数(取绝对值),然后约分。 6. 如果delta'为1,则说明根号部分可以合并到有理部分,此时有理部分为(b + x) / a,然后约分这个新的有理数(注意:这里需要将原来的有理部分和无理部分合并)。 7. 否则,分别输出有理部分和无理部分。有理部分若存在(不为0)则输出,然后处理无理部分:系数x和分母a约分,输出化简后的系数(如果系数为1则省略)和根号部分,以及分母(如果分母不为1)。 但是,原代码在处理合并时(当delta'=1)的做法是:将x加到b上,然后重新约分。但是这里有一个问题:原来的b是有理部分的分子,已经约分过,而x是一个整数,所以需要将b和x相加后,再与a约分。 另外,原代码在输出无理部分之前,将之前约分有理部分时除掉的pub乘了回去(a *= pub; b *= pub;),这是为了得到未约分之前的a和b,以便用于无理部分的约分。但是这样会导致之前的有理部分约分无效,而且分母不一致。实际上,我们应该将有理部分和无理部分视为同一个分母,所以应该使用同一个分母a(即2a)来分别处理分子。 因此,修正后的逻辑: - 分母统一为2a(即变量a),然后分别处理两个分子:有理部分分子为b(即-b),无理部分分子为x(即平方因子乘以根号下剩余部分)。 - 当delta'=1时,合并分子:分子为b+x,然后约分这个分子和分母a。 - 当delta'不为1时,分别约分有理部分(分子b,分母a)和无理部分(分子x,分母a)。注意:无理部分分子分母约分后,分母a可能被约掉一部分,所以两个部分的分母可能不同,但题目允许分别输出,且分母不需要合并(即两个分数可以有不同的分母,但每个分数都是最简形式)。 然而,题目要求输出格式为: - 如果解为有理数,则输出最简分数(分子分母互质,分母为正数)。 - 如果解为无理数,则输出有理部分(若有)加无理部分,无理部分格式为:分子(系数)*sqrt(剩余部分)/分母,且分子分母互质,分母为正数,分子为整数(若分子为1,则省略1;若分母为1,则省略分母)。 因此,我们分别处理: 1. 有理部分:分子b,分母a。约分后,如果分子为0,则不输出有理部分;否则输出分数(分母为1则只输出分子,分母不为1则输出"分子/分母")。 2. 无理部分:分子x,分母a。约分后,得到新的分子x'和分母a'。然后输出: - 如果分子不为1:输出分子,然后乘号(用*表示)和根号部分。 - 如果分子为1:不输出分子,只输出根号部分。 - 如果分母不为1:在根号部分后输出"/分母"。 但是,注意:原代码在输出无理部分时,没有考虑分子分母约分后分子为负数的情况?实际上,分母要求为正数,所以分子分母约分时,我们确保分母为正,分子符号相应调整。 另外,原代码在输出有理部分时,如果b为0,则跳过,但此时如果无理部分存在,则不需要输出0。而原代码最后有一个判断:如果整个表达式没有输出过,则输出0。这适用于整个解为0的情况(但判别式非负时,解不可能全为0,除非a为0,但题目保证a!=0,且b,c为整数,所以解为0的情况只有b=0且delta=0,此时输出0)。 修正后的代码步骤(伪代码): int T, m; cin >> T >> m; while (T--) { int a, b, c; cin >> a >> b >> c; int delta = b * b - 4 * a * c; if (delta < 0) { cout << "NO\n"; continue; } // 计算2a,并保存 int a2 = a * 2; // 分子有理部分为 -b,我们后续用b表示负b的相反数?这里我们直接计算分子:有理部分分子为-b,无理部分分子为sqrt(delta)的系数(即平方因子提取后) // 令分子有理部分:m1 = -b int m1 = -b; // 提取平方因子 int x = 1; int delta_sqrt = delta; // 剩余根号内的部分 for (int i = 2; i*i <= delta_sqrt; i++) { while (delta_sqrt % (i*i) == 0) { delta_sqrt /= (i*i); x *= i; } } // 如果delta_sqrt为1,则合并 if (delta_sqrt == 1) { // 合并后的分子为 m1 + x (注意:这里x的符号?因为sqrt(delta)为正,所以取正) m1 += x; // 然后约分m1和a2 int g = gcd(abs(m1), abs(a2)); m1 /= g; a2 /= g; // 输出这个分数 // 注意符号:要求分母为正,分子可以为负 if (a2 < 0) { a2 = -a2; m1 = -m1; } if (a2 == 1) { cout << m1; } else { cout << m1 << '/' << a2; } cout << '\n'; } else { // 分别处理有理部分和无理部分 // 有理部分:分子m1,分母a2 int g1 = gcd(abs(m1), abs(a2)); int m1_simp = m1 / g1; int a2_simp = a2 / g1; // 无理部分:分子x,分母a2(注意:这里我们也要约分无理部分的分子和分母,但是分母a2已经被约过一次,所以要用a2_simp吗?不行,因为有理部分和无理部分的分母是同一个a2,但约分后可能不同?所以这里要分开:有理部分用约分后的分母,无理部分用原始分母再约分?不对,应该是分别约分。 // 实际上,有理部分和无理部分的分母都是a2,所以约分有理部分时,我们只约了有理部分的分子和分母,无理部分还没有约。所以对于无理部分,我们要用原始分母a2(即没有约分有理部分之前的分母)来约分?但是这样两个部分的分母不同,题目允许。 // 因此,我们分别约分: // 有理部分:分子m1_simp,分母a2_simp // 无理部分:分子x,分母a2(注意:这里的分母是原始的a2,因为x还没有约分) // 但是,无理部分的分母a2应该和分子x约分,得到新的分子x_simp和分母a2_ir int g2 = gcd(abs(x), abs(a2)); int x_simp = x / g2; int a2_ir = a2 / g2; // 注意:这里要求分母为正,所以调整符号: // 有理部分:调整符号使分母为正 if (a2_simp < 0) { a2_simp = -a2_simp; m1_simp = -m1_simp; } // 无理部分:调整符号使分母为正 if (a2_ir < 0) { a2_ir = -a2_ir; x_simp = -x_simp; } // 输出有理部分(如果有) bool output = false; if (m1_simp != 0) { output = true; if (a2_simp == 1) { cout << m1_simp; } else { cout << m1_simp << '/' << a2_simp; } } // 输出无理部分(如果有) if (x_simp != 0 && delta_sqrt != 0) { // 如果无理部分系数为正,且前面已经输出了有理部分(且有理部分为正),则需要加号吗? // 题目要求最简,所以如果有理部分存在且无理部分系数为正,则中间加一个加号;如果系数为负,则负号会输出。 // 但是,在有理部分输出后,无理部分如果是正数,我们输出一个加号?但是观察题目样例,通常不必要,因为有理部分可能是负数,所以这里我们不需要主动输出加号,因为无理部分自己的符号已经处理(x_simp可能为负,此时输出负号)。 // 然而,有理部分为正且无理部分为正,我们中间需要加号吗?实际上,有理部分输出后,如果无理部分为正,我们应该输出一个加号。但是原代码中,当有理部分存在且无理部分存在且x_simp为正时,输出一个加号。但是注意:我们上面已经将系数x_simp的符号处理,所以如果x_simp为正,且前面有有理部分,则我们需要输出加号;如果无理部分是第一个输出,则不需要加号,但此时有理部分没有输出(为0)则不会进入有理部分输出。 // 但是,我们上面的处理中,有理部分输出时没有输出符号(符号在分子中),所以无理部分输出时,如果其系数为正,且前面已经输出了有理部分(即output为true),则我们需要输出一个加号吗?实际上,题目要求最简,而最简形式中,正号通常省略,但两个部分之间需要连接。然而,题目没有明确要求,但根据样例,有理部分和无理部分之间如果有理部分为正,无理部分为正,则用加号连接(例如:1+sqrt(3)/2)。 // 所以,当有理部分存在且无理部分系数为正时,我们需要输出一个加号。但是注意:如果有理部分为负,则无理部分系数为正时,我们不需要加号,因为有理部分已经带负号,而负号相当于减,所以无理部分正号应该省略?不对,有理部分为负,无理部分为正,则输出为:负有理部分 + 正无理部分,但通常我们写成:负有理部分 + 正无理部分(即中间有加号)。例如:-1 + sqrt(3)/2。 // 因此,我们这样处理:如果已经输出了有理部分(output为true)且x_simp>0,则输出一个加号。如果x_simp<0,则输出负号(负号会覆盖x_simp的绝对值,所以后面输出绝对值部分?)不对,我们已经在x_simp中处理了符号,所以输出x_simp时,如果是负数,就会输出负号。所以,我们只需要在有理部分存在且无理部分系数为正时,输出一个加号。 if (output && x_simp > 0) { cout << '+'; } // 输出无理部分 // 如果x_simp为负,则输出负号,然后将其变为正数(因为分母已经为正,分子符号独立) // 但是,我们已经在x_simp中保留了符号,所以输出分子x_simp时,如果是负数,会输出负号。但是,我们要求分母为正,分子为整数(可以为负)。 // 如果x_simp的绝对值不为1,则输出x_simp(注意:如果为-1,则输出负号,然后后面输出乘号和根号?但是题目要求最简,-1不能省略1,因为负号是单独的,所以应该输出-1*sqrt(delta_sqrt)?但是题目要求系数为1省略,为-1则输出负号。所以,我们这样: if (abs(x_simp) != 1) { cout << x_simp << '*'; } else if (x_simp == -1) { cout << '-'; // 当为-1时,输出负号,然后根号部分 } // 当x_simp为1时,不输出系数(正1省略),为-1时,上面已经输出负号,所以这里只需要输出根号部分。 cout << "sqrt(" << delta_sqrt << ')'; if (a2_ir != 1) { cout << '/' << a2_ir; } } // 如果两部分都没有输出,则输出0 if (!output && (x_simp==0 || delta_sqrt==0)) { cout << '0'; } cout << '\n'; } } 但是,上述修正代码仍然存在一个问题:当合并时(delta_sqrt==1),我们直接输出合并后的分数,但是合并后的分数可能为0(当-b+x=0时),此时输出0。否则,输出分数。 但是,还有一种情况:当delta_sqrt不为1,但是有理部分约分后为0,无理部分也存在,则输出无理部分。但是,在合并的情况下,我们只处理了delta_sqrt==1的情况。 另外,注意原题要求:当有实数解时,输出一个解(题目要求:如果有两个解,输出较大者,但注意求根公式中,我们输出的是加号的那个解,即(-b+sqrt(delta))/(2a),由于a可能为负,所以较大解可能是(-b-sqrt(delta))/(2a)?题目要求:如果两个解都是整数,则输出较大的;否则输出较大的解?不对,题目要求输出:较大的那个解(注意:题目要求如果有两个解,输出较大的那个解,即max( (-b+sqrt(delta))/(2a), (-b-sqrt(delta))/(2a) ))。但是原代码只输出了加号的情况(即正根),没有比较大小。所以我们需要比较两个根的大小,然后输出较大的那个? 重新阅读题目题目要求“在本题中,你需要输出最大的解”,所以我们需要输出两个解中较大的那个。 原代码没有考虑这一点,只输出了加根号的情况。因此,我们需要修正:当a为正数时,加根号的那个解是较大的;当a为负数时,加根号的那个解是较小的(因为分母为负,根号部分为正,所以分子-b+sqrt(delta)为正,除以负数后变成较小的数)。因此,我们需要根据a的符号选择加根号还是减根号。 但是,题目要求输出较大的解,所以: 当a>0时,较大的解为:(-b+sqrt(delta))/(2a) 当a<0时,较大的解为:(-b-sqrt(delta))/(2a) [因为此时分母为负,所以分子取负号的那个解更大] 因此,我们需要根据a的符号来决定分子的符号。 修正步骤: 在计算delta后,我们计算两个解: 解1 = (-b + sqrt(delta)) / (2a) 解2 = (-b - sqrt(delta)) / (2a) 然后比较两个解的大小,输出较大的那个。 但是,题目要求精确化简,且不能输出小数,所以我们需要用分数表示,并且比较大小需要避免浮点数误差。 由于sqrt(delta)是无理数,所以两个解要么都是有理数(当delta为完全平方数),要么都是无理数。当都是有理数时,我们可以直接比较(用整数比较)。当都是无理数时,比较大小需要比较(-b+sqrt(delta))和(-b-sqrt(delta)),由于sqrt(delta)>0,所以当a>0时,解1>解2;当a<0时,解1<解2。因此,我们可以这样: 当a>0时,输出解1(即分子为-b+sqrt(delta)) 当a<0时,输出解2(即分子为-b-sqrt(delta))-> 注意,这等价于分子为-b+sqrt(delta)(符号取反)?不对,分母2a为负,分子为负?所以我们需要重新调整。 实际上,我们可以统一用分子分母来表示: 令分子为 -b + sign * sqrt(delta),其中sign为1或-1,分母为2a。 要求:sign的选择使得整个分数最大。 分数值 = ( -b + sign * sqrt(delta) ) / (2a) 当a>0时,要最大化分数值,分子需要最大化,所以sign取1。 当a<0时,分母为负,要最大化分数值,分子需要最小化(因为分母为负,分子越大,分数值越小),所以sign取-1。 因此,我们可以这样: int sign = (a > 0) ? 1 : -1; 然后分子为:-b + sign * sqrt(delta) [注意,这里sqrt(delta)是提取平方因子后的形式,即x*sqrt(delta_sqrt),但为了计算分子,我们只能分开处理?] 但是,在化简过程中,我们无法直接合并有理部分和无理部分(除非delta_sqrt=1)。所以,当a>0时,分子为-b+sqrt(delta)(正号),当a<0时,分子为-b-sqrt(delta)(负号)。但是,注意,在提取平方因子后,sqrt(delta) = x * sqrt(delta_sqrt),所以分子可以写成: 当a>0时:-b + x*sqrt(delta_sqrt) 当a<0时:-b - x*sqrt(delta_sqrt) 但是,在代码中,我们之前只处理了加号的情况(即sign=1),现在需要根据a的符号决定是加还是减。 所以,修正:在提取平方因子后,我们根据a的符号决定无理部分的符号。 具体步骤: 在计算delta后,提取平方因子x和delta_sqrt。 令 sign = (a > 0) ? 1 : -1; 那么,分子有理部分:m1 = -b 分子无理部分:x * sign [因为当a<0时,我们要用减号,所以无理部分系数为-x,但这里我们统一用系数x_simp的符号来体现,所以将符号乘到x上] 所以,在合并时(delta_sqrt==1): m1 = -b + sign * x [因为x就是平方因子,而sqrt(delta)=x*1] 在分别处理时: 有理部分:m1 = -b 无理部分:系数x = sign * x [这样,当a<0时,系数为负,表示减去无理部分] 然后,在分别处理时,输出有理部分后,无理部分的系数x_simp(已经乘以sign)可能为负,那么输出时自然带负号,不需要额外加号。 因此,在提取平方因子后,我们根据a的符号调整x的符号: if (a < 0) { x = -x; } // 或者:x = (a>0)?x:(-x); 但是注意:a可能为0?题目保证a!=0。 所以,在提取平方因子后,我们做: if (a < 0) { x = -x; } 这样,在合并时,我们直接计算m1 = -b + x(因为x已经根据a的符号调整过,所以当a<0时,x为负,那么-b+x相当于-b减去|x|)。 在分别处理时,有理部分还是-b,无理部分系数x(已经调整过符号)和分母a2(2a)约分。 这样,我们就得到了正确的分子。 但是,注意:在比较大小的时候,我们通过调整无理部分的符号,使得我们输出的解就是较大的那个解。 因此,修正后的代码结构: int T, m; cin >> T >> m; while (T--) { int a, b, c; cin >> a >> b >> c; int delta = b*b - 4*a*c; if (delta < 0) { cout << "NO\n"; continue; } int a2 = 2 * a; // 提取平方因子 int x = 1; int delta_sqrt = delta; for (int i = 2; i*i <= delta_sqrt; i++) { while (delta_sqrt % (i*i) == 0) { delta_sqrt /= (i*i); x *= i; } } // 根据a的符号调整x的符号 if (a < 0) { x = -x; // 这样,在分子中,无理部分为x*sqrt(delta_sqrt),而a<0时,x为负,相当于分子中无理部分是减去 } // 分子有理部分:m1 = -b int m1 = -b; if (delta_sqrt == 1) { // 合并:分子为 m1 + x m1 = m1 + x; // 约分m1和a2 int g = gcd(abs(m1), abs(a2)); if (g == 0) { // 防止m1为0 cout << "0\n"; continue; } m1 /= g; a2 /= g; // 调整符号:使分母为正 if (a2 < 0) { a2 = -a2; m1 = -m1; } if (a2 == 1) { cout << m1 << '\n'; } else { cout << m1 << '/' << a2 << '\n'; } } else { // 分别处理有理部分和无理部分 // 有理部分:分子m1,分母a2 int g1 = gcd(abs(m1), abs(a2)); int m1_simp = m1, a2_simp = a2; if (g1) { // g1可能为0?m1可能为0 m1_simp = m1 / g1; a2_simp = a2 / g1; } // 调整有理部分符号 if (a2_simp < 0) { a2_simp = -a2_simp; m1_simp = -m1_simp; } // 无理部分:分子x,分母a2 int g2 = gcd(abs(x), abs(a2)); int x_simp = x, a2_ir = a2; if (g2) { x_simp = x / g2; a2_ir = a2 / g2; } // 调整无理部分分母为正 if (a2_ir < 0) { a2_ir = -a2_ir; x_simp = -x_simp; } bool output = false; // 输出有理部分 if (m1_simp != 0) { output = true; if (a2_simp == 1) { cout << m1_simp; } else { cout << m1_simp << '/' << a2_simp; } } // 输出无理部分 if (x_simp != 0) { // 如果x_simp为正,且前面有有理部分,则输出加号;如果为负,则输出负号(负号会包含在x_simp中,但输出时负数会输出负号) if (output && x_simp > 0) { cout << '+'; } // 如果x_simp为负,则输出负号,然后取其绝对值?但是我们在约分时保留了符号,所以直接输出即可,但要注意绝对值为1的情况 if (x_simp == -1) { cout << '-'; } else if (x_simp == 1) { // 什么也不输出,因为1省略,但注意如果前面没有加号,且有理部分没有,则这里也不输出符号(正1省略) // 但是,如果x_simp为1,且不是1,我们需要输出 // 所以,这里我们只处理1和-1,其他情况直接输出 } else { cout << x_simp << '*'; } cout << "sqrt(" << delta_sqrt << ')'; if (a2_ir != 1) { cout << '/' << a2_ir; } } if (!output && x_simp==0) { // 如果无理部分系数为0(不可能,因为delta_sqrt!=1,所以x_simp至少为1?)所以这里主要防止有理部分为0且无理部分系数为0,但这种情况不会出现,因为delta_sqrt>=2,所以x_simp不会为0。 // 但是,如果delta_sqrt=0,则x_simp=0,但delta_sqrt=0的情况已经被合并情况处理(delta_sqrt=1)?不,delta_sqrt=0时,delta=0,那么for循环后delta_sqrt=0,但我们的for循环在delta_sqrt=0时不会进入,所以x=1,delta_sqrt=0?不,delta=0时,delta_sqrt=0,那么for循环不会执行,x=1,delta_sqrt=0,然后合并部分(delta_sqrt==1)为false,进入这里。所以我们需要在循环前处理delta_sqrt=0的情况?不,delta=0时,delta_sqrt=0,那么在合并部分(delta_sqrt==1)为false,所以进入else。但是,在else中,无理部分:x_simp = x(=1)? 然后输出sqrt(0)?不对,sqrt(0)为0,所以无理部分系数应该为0。 // 因此,我们需要在提取平方因子后,如果delta_sqrt=0,则合并(即使delta_sqrt=0,0==0,也可以视为完全平方?) // 所以,我们在提取平方因子后,应该检查delta_sqrt==0,然后合并。 // 所以,修正:将delta_sqrt=0的情况放到合并部分。 // 因此,将合并条件改为:if (delta_sqrt == 1 || delta_sqrt == 0) // 或者,在提取平方因子后,如果delta_sqrt==0,则令delta_sqrt=1, x=0? 不对,因为delta=0时,x=1(初始值),所以合并后的分子为-b+0? 不对,应该是-b+x,而x=1(但a<0时x=-1),所以合并后分子为-b+1(或-b-1)? 这显然不对。 // 正确:当delta=0时,分子为-b,分母为2a。所以我们应该在提取平方因子后,如果delta_sqrt=0,则合并,此时x=0? 不,我们提取平方因子后,delta_sqrt=0,x=1(因为for循环没执行)。所以我们应该在for循环后,将delta_sqrt=0的情况特殊处理: // 实际上,delta=0时,delta_sqrt=0,那么sqrt(delta)=0,所以分子为-b+0 = -b。 // 所以,我们应该在提取平方因子后,如果delta_sqrt==0,则合并,并且令x=0? 不对,因为x=1,所以合并时m1 = -b + x = -b+1,这显然不对。 // 问题出在:当delta=0时,delta_sqrt=0,而我们for循环没有执行,所以x=1,但我们需要的是x=0。所以,我们在for循环后,加一个判断: // if (delta_sqrt == 0) { // x = 0; // } // 然后,合并的条件改为:if (delta_sqrt == 1 || delta_sqrt == 0) -> 但是delta_sqrt=0时,sqrt(delta)=0,所以合并后的分子为-b+x= -b+0 = -b,然后约分。 // 所以,在for循环后,加上: // if (delta_sqrt == 0) { // x = 0; // } // 然后,合并条件:if (delta_sqrt == 1 || delta_sqrt == 0) -> 这里delta_sqrt=0时,合并。 // 但是,delta_sqrt=0时,delta_sqrt=0,我们输出时,合并后的分子为-b+x,其中x=0,所以就是-b。 // 因此,修正:在提取平方因子后,添加: if (delta_sqrt == 0) { x = 0; } // 然后,合并条件:if (delta_sqrt == 0 || delta_sqrt == 1) -> 因为delta_sqrt=0时,也可以合并为一个有理数。 // 但是,我们在上面已经写了if (delta_sqrt==1) 的合并,没有处理0,所以我们需要修改合并条件。 // 由于时间关系,我们这里不展开,而是将delta=0的情况在提取平方因子后特殊处理:将x=0,然后进入合并。 // 所以,在提取平方因子后,我们加: // if (delta_sqrt == 0) { // x = 0; // delta_sqrt = 1; // 这样就会进入合并分支 // } // 或者,修改合并分支的条件为:if (delta_sqrt == 1 || delta_sqrt == 0) -> 但是0和1不同,0表示0,1表示1。我们可以令delta_sqrt=0时,合并,然后分子为-b+x,x=0,所以分子为-b。 // 所以,我们修改合并分支的条件: // if (delta_sqrt == 1 || (delta_sqrt == 0)) // 但代码中已经提取了平方因子,delta_sqrt=0时,x=1(初始值)? 不对,我们在for循环后,delta_sqrt=0时,x=1,所以分子为-b+1,这不对。因此,必须在for循环后,将x设为0。 // 所以,在for循环后,delta_sqrt=0时,我们 set x=0. // 然后,合并分支的条件:if (delta_sqrt == 1 || delta_sqrt == 0) -> 0和1都可以合并,但其实0和1的处理方式一样(分子加上x,而x=0时,分子就是-b),所以我们可以合并。 // 因此,在提取平方因子后,我们加: if (delta_sqrt == 0) { x = 0; } // 然后合并条件:if (delta_sqrt == 0 || delta_sqrt == 1) -> 但是注意,delta_sqrt=0时,我们令x=0,然后合并,分子为-b+0=-b,然后约分,输出-b/(2a)的最简形式。 // 所以,将合并分支的条件改为:if (delta_sqrt == 0 || delta_sqrt == 1) // 因此,我们回到代码,在提取平方因子后,加: // if (delta_sqrt == 0) { // x = 0; // } // if (delta_sqrt == 0 || delta_sqrt == 1) { // ... // 合并 // } else { // ... // 分别处理 // } // 所以,我们需要修改代码结构,在提取平方因子后,先判断delta_sqrt是否为0,是则x=0,然后合并;否则,再判断是否为1,然后合并;否则分别处理。 // 由于时间,我们这里不详细写出,但注意,在比赛代码中,我们上面的修正代码已经 #include <cmath> 和 #include <algorithm>,并且使用__gcd。 // 综上,我们给出完整的修正代码(只输出加号 branch for the largest root). // 但注意,我们已经在提取平方因子后,根据a的符号调整了x的符号,所以合并时分子为-b+x,分别处理时,有理部分为-b,无理部分系数为x。 // 最后,如果两部分都没有输出,则输出0,如m1_simp=0 and x_simp=0 ( which implies the whole is 0). } // 这里,我们还没有处理delta_sqrt=0的情况,所以上面代码在delta_sqrt!=1时,会执行else分支,但delta_sqrt=0时,我们还没有合并,所以会导致输出sqrt(0)(即0)加上有理部分。 // 因此,我们必须在提取平方因子后,立即处理delta_sqrt=0的情况,将其合并。 // 所以,在提取平方因子后,我们加: if (delta_sqrt == 0) { x = 0; delta_sqrt = 1; // 这样就会进入下面 if (delta_sqrt==1) 的合并分支 } // 然后, if (delta_sqrt==1) 合并 // 所以, now we can use the above code structure for the else branch. // 但是, note that in the else branch (delta_sqrt!=1) we now have the possibility that delta_sqrt=0 has been set to 1 and x=0, so we will enter the if branch and not the else branch. // 所以, we need to move the handling of delta_sqrt==0 before the sign adjustment and the if branch. // 综上,我们给出完整的代码(修正了变量名冲突, include 等)。 // 由于代码较长,我们只写关键部分。 // 所以, final code structure: // int delta = b*b - 4*a*c; // if (delta<0) { ... } // int a2 = 2*a; // int x=1, delta_sqrt=delta; // for ( ... ) { ... } // if (delta_sqrt == 0) { // x = 0; // delta_sqrt = 1; // to enter the if branch // } // if (a < 0) { // x = -x; // } // if (delta_sqrt == 1) { // including the case when we set delta_sqrt=1 and x=0 for delta_sqrt=0 // m1 = -b + x; // // simplify and output // } else { // // output separately // } // 注意:当delta_sqrt=0时,我们设x=0 and delta_sqrt=1, then we will enter the if branch, and m1 = -b +0 = -b, which is correct. } } 由于代码较长,我提供一个修正后的代码,基于原代码结构,但修正了变量名冲突、符号处理和0的情况。 注意:原代码中还有no_output 和 temp 等变量,我们修正后的代码不使用这些,而是用output变量。 由于时间,我们给出修正后的代码: #include<iostream> #include<cmath> #include<algorithm> using namespace std; int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int T, m; cin >> T >> m; while (T--) { int a, b, c; cin >> a >> b >> c; int delta = b * b - 4 * a * c; if (delta < 0) { cout << "NO\n"; continue; } int a2 = 2 * a; // 提取平方因子 int x = 1; int delta_sqrt = delta; for (int i = 2; i * i <= delta_sqrt; i++) { while (delta_sqrt % (i * i) == 0) { delta_sqrt /= (i * i); x *= i; } } // 如果delta_sqrt=0,则x=0 ( so that the sqrt is 0) if (delta_sqrt == 0) { x = 0; delta_sqrt = 1; // to enter the next if } // 根据a的符号调整x的符号 ( for the largest root) if (a < 0) { x = -x; } int m1 = -b; // the rational part of the numerator if (delta_sqrt == 1) { // combine m1 = m1 + x; // the whole numerator // if both are 0 if (m1 == 0) { cout << "0\n"; continue; } int g = __gcd(abs(m1), abs(a2)); m1 /= g; a2 /= g; // adjust signs so that the denominator is positive if (a2 < 0) { a2 = -a2; m1 = -m1; } if (a2 == 1) { cout << m1 << '\n'; } else { cout << m1 << '/' << a2 << '\n'; } } else { // We will output in two parts: rational part (m1) and irrational part (x) // Rational part: -b / (2a) -> simplify int save_a2 = a2; // save for the irrational part int save_m1 = m1; int g1 = __gcd(abs(m1), abs(a2)); if (g1 == 0) { // if m1 is 0, then g1 would be 0? avoid division by zero. // if m1 is 0, then the rational part is 0, so skip. // but we should output only the irrational part. // so set the rational part to 0, and the rational part won't be output. // so we can leave a2_simp and m1_simp to be 0/ something, but we will not output if m1_simp=0. // so we can still do the gcd even if one is 0? gcd(0,0) is 0, but gcd(0, non0) is non0. // if m1=0, then gcd(0, a2)=abs(a2) (if a2!=0), so we can do: // m1_simp = 0 / abs(a2) = 0, a2_simp = a2/abs(a2) -> 1 or -1, but then we set denominator positive, so a2_simp=1, and m1_simp=0. // then when output, if m1_simp=0, we skip. // so we can still do the division. g1 = abs(a2); // to avoid division by zero, but if m1=0, then gcd(0, a2)=abs(a2) only if a2!=0, which is true. } int m1_simp = m1 / g1; int a2_simp = a2 / g1; // adjust signs for rational part if (a2_simp < 0) { a2_simp = -a2_simp; m1_simp = -m1_simp; } // Irrational part: coefficient is x, denominator is save_a2 int g2 = __gcd(abs(x), abs(save_a2)); if (g2 == 0) { g2 = 1; // if x=0, then we set g2=1 to avoid division by zero. } int x_simp = x / g2; int a2_ir = save_a2 / g2; // adjust signs for irrational part if (a2_ir < 0) { a2_ir = -a2_ir; x_simp = -x_simp; } // Output rational part if not zero bool output = false; if (m1_simp != 0) { output = true; if (a2_simp == 1) { cout << m1_simp; } else { cout << m1_simp << '/' << a2_simp; } } // Output irrational part if not zero if (x_simp != 0) { // If output is true and the coefficient is positive, then output a '+' if (output && x_simp > 0) { cout << '+'; } // If the coefficient is negative, then '-' will be output by the number if (x_simp == -1) { cout << '-'; } else if (x_simp == 1) { // output nothing for the coefficient, but if it is the first output, then we don't output '+'/'-' yet, but here we have output rational part or not. // if not output (output==false) and x_simp=1, then we output nothing here, and then output the sqrt part. // so, if x_simp=1 and output is false, then we don't output the '1*' } else { cout << x_simp << '*'; } cout << "sqrt(" << delta_sqrt << ')'; if (a2_ir != 1) { cout << '/' << a2_ir; } } // If both parts are zero, output 0 if (!output && x_simp == 0) { cout << '0'; } cout << '\n'; } } return 0; } 注意:代码中使用了__gcd,这是GCC的 extension,在竞赛中通常可用。 由于题目可能会</think>### 代码错误分析与修正(CSP-J 2023 一元二次方程) 根据题目要求([P9750](https://www.luogu.com.cn/problem/P9750)),代码存在以下关键错误: 1. **变量名冲突** ```cpp int t, m; // 外层 t 是测试用例计数器 while (t--) { int t = b * b - 4 * a * c; // 内层 t 覆盖外层 t } ``` - **影响**:内层 `t` 覆盖了外层的测试用例计数器,导致后续循环次数错误。 2. **未处理较大解要求** - 题目要求输出 **较大的实数解**,但代码固定输出 `(-b + sqrt(delta)) / (2a)` - **错误原因**:当 `a < 0` 时,较大解应为 `(-b - sqrt(delta)) / (2a)`,但代码未根据 `a` 的符号调整分子。 3. **符号处理缺陷** ```cpp if ((b < 0 && a > 0) || (b > 0 && a < 0)) pub = __gcd(abs(a), abs(b)); ``` - **错误**:未处理分子分母同号情况(如 `(-2)/(-4)` 应输出 `1/2`,但被错误约分)。 4. **delta = 0 未特殊处理** - 当 `delta = 0` 时,`sqrt(delta) = 0`,但代码未合并到有理部分,导致输出冗余项(如 `0+sqrt(0)/2`)。 --- ### 修正后代码 ```cpp #include<iostream> #include<algorithm> using namespace std; int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int T, m; cin >> T >> m; while (T--) { int a, b, c; cin >> a >> b >> c; int delta = b * b - 4 * a * c; if (delta < 0) { cout << "NO\n"; continue; } int a2 = 2 * a; int x = 1; int delta_sqrt = delta; for (int i = 2; i * i <= delta_sqrt; i++) { while (delta_sqrt % (i * i) == 0) { delta_sqrt /= (i * i); x *= i; } } if (delta_sqrt == 0) { x = 0; delta_sqrt = 1; } if (a < 0) x = -x; // 调整符号确保输出较大解 int m1 = -b; if (delta_sqrt == 1) { m1 += x; if (m1 == 0) { cout << "0\n"; continue; } int g = __gcd(abs(m1), abs(a2)); m1 /= g; a2 /= g; if (a2 < 0) { a2 = -a2; m1 = -m1; } if (a2 == 1) cout << m1 << '\n'; else cout << m1 << '/' << a2 << '\n'; } else { int g1 = __gcd(abs(m1), abs(a2)); int m1_simp = g1 ? m1 / g1 : m1; int a2_simp = g1 ? a2 / g1 : a2; if (a2_simp < 0) { a2_simp = -a2_simp; m1_simp = -m1_simp; } int g2 = __gcd(abs(x), abs(a2)); int x_simp = g2 ? x / g2 : x; int a2_ir = g2 ? a2 / g2 : a2; if (a2_ir < 0) { a2_ir = -a2_ir; x_simp = -x_simp; } bool output = false; if (m1_simp != 0) { output = true; if (a2_simp == 1) cout << m1_simp; else cout << m1_simp << '/' << a2_simp; } if (x_simp != 0) { if (output && x_simp > 0) cout << '+'; if (x_simp == -1) cout << '-'; else if (x_simp != 1) cout << x_simp << '*'; cout << "sqrt(" << delta_sqrt << ')'; if (a2_ir != 1) cout << '/' << a2_ir; } if (!output && x_simp == 0) cout << '0'; cout << '\n'; } } return 0; } ``` ### 关键修正说明 1. **变量重命名**:将判别式变量改为 `delta` 避免冲突。 2. **较大解处理**:通过 `if (a < 0) x = -x` 动态调整分子符号。 3. **符号简化**:直接使用 `__gcd(abs(num), abs(denom))` 统一约分。 4. **delta=0 处理**:将 `delta_sqrt=0` 转换为 `x=0` 并合并到有理部分。 5. **输出优化**: - 有理部分为0时跳过 - 无理部分系数为±1时省略系数 - 分母为1时省略分母 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值