东华OJ基础题第54题等差数列题目本身很短输入三个数分别代表首项a、公差b和项数n输出前n项的和。很多同学刚接触C时就是从这类题起步的但正因为简单它反而特别适合用来检验C基础扎不扎实——变量类型、循环结构、表达式运算、输入输出任何一个环节有漏洞都会在这一题上露馅。这篇文章我就用做题的角度把这道题从头拆到尾包括两种主流写法的对比、数据类型的真实边界、公式法背后的运算顺序陷阱、OJ判题系统返回的常见状态是什么意思以及我把这道题改造成几个变式练手的心得。主线是东华OJ的原题但里面讲到的坑放到其他OJ的基础题里一样成立。1. 题目分析与解题思路1.1 题目到底在考什么先看题面东华OJ第54题“等差数列”通常会这样描述给定首项a、公差b、项数n求等差数列前n项之和。也就是说序列是 a, ab, a2b, ..., a(n-1)b把这一共n个数加起来输出。基础题定位下的“考什么”实际有三层第一层是读题能力。你要能识别出a是首项、b是公差、n是项数并且知道求和的对象是“前n项”不是第n项。很多新手的第一个错误就是把第n项的值当成答案提交。第二层是算法选择。求和既可以暴力循环累加n次也可以直接用等差数列求和公式一步算出。基础题里这两种做法都能过但背后的复杂度思维完全不同。第三层是代码基本功。变量类型选对没有、公式会不会因为运算顺序溢出、输入输出格式对不对这些才是一道基础题真正想让你练的东西。东华OJ这类系统评测非常严格答案错一个字节都算WA。把这三层想清楚再看这道题就明白它为什么被放进“基础题”里了。它不考你刁钻的思维考的恰恰是你写C时最容易忽略的三个环节类型、边界、表达式的求值顺序。1.2 公式法与循环法为什么我推荐公式写这道题最直白的方案是循环long long sum 0; for (long long i 0; i n; i) { sum a i * b; }循环i从0到n-1每次累加第i项也就是a加上i倍的公差。逻辑上没有任何问题n比较小的时候性能也完全够。但等差数列求和有一个现成的公式前n项和 S 可以写成S n * a n * (n - 1) / 2 * b推导思路很简单n个a加起来贡献了na剩下的是公差带来的增量0 1 2 ... (n-1) n(n-1)/2每项增量都是b所以再乘以b就是总增量。公式法和循环法之间的取舍本质上是在“代码可读性”和“时间复杂度”之间做权衡写法时间复杂度代码量适用场景循环累加O(n)较长n较小时直观易懂公式求解O(1)极短任意规模推荐有些同学觉得公式法不够直观担心自己记错。实际上公式法最大的优点不只是快而是快得很稳定。如果题目把n开到10的9次方循环版跑10亿次OJ上大概率超时TLE公式版瞬间出结果。我的建议是基础题阶段就把“能选公式就选公式”这个习惯养成。这不是炫技是算法题的基本素养——能用数学性质化简的就不该拿循环去硬撑。2. 核心细节与常见误区2.1 为什么必须用 long long这是第54题最容易被坑的地方也是我见过提交WA率最高的原因。题目没有明确给a、b、n的取值范围但作为OJ基础题数据往往不会太大有些同学就直接写了int。int在32位平台下能表示的最大值是2147483647也就是2.1亿左右。看起来很大但在这个公式里完全不够看。我们算一笔账假设n等于10亿也就是1000000000那么 n * (n - 1) / 2 的值是多少1000000000乘以999999999大约是999999999000000000再除以2得到499999999500000000。这个数字是接近5乘以10的17次方的远超int能容纳的2.1乘以10的9次方。就算n、a、b单独看都不大公式中间量也可能瞬间爆炸。举个更实际的例子a 1, b 1, n 100000前n项和是5000050000这个结果本身已经超过int的21亿上限了。最终和都装不下更别说中间量n*(n-1)。所以最稳妥的做法是把a、b、n和sum全部声明为long long也就是让所有计算都发生在64位环境下。long long的最大值约为9.2乘以10的18次方覆盖这类求和绰绰有余。2.2 运算顺序的隐藏陷阱公式 S na n(n-1)/2*b 里藏着一个很容易被忽略的细节乘法要先做还是除法要先做。很多同学写代码时习惯从左到右写n * (n - 1) / 2 * b。这个写法在C里是从左往右计算的先计算n*(n-1)再除以2最后乘b。由于n和(n-1)都是long long这个中间乘积的数值规模已经很大了但只要没超过long long的极限就没问题。另一个常见写法是 n * (n - 1) * b / 2也就是先乘完所有数再除以2。这种写法问题更大。n*(n-1)已经很大了再乘以b整体数值又放大了一轮long long也会扛不住。正确姿势是利用连续整数相乘必有一个偶数的性质先算n*(n-1)/2。这个除法是整除结果恰好是整数不会产生小数误差。更重要的是它把最大的中间量n*(n-1)先“压缩”下来再去乘b整个表达式的数值规格小很多。我还见过一种更夸张的写法把公式写成 n * a n * (n - 1) / 2 * b但n、a、b全是int只在最后赋值给long long sum。这种做法本质上没有任何保护作用。因为C是先按int完成表达式运算再把结果转成long long中间早就溢出了最后转出来的也是错的。要杜绝这种问题最省心的方法就是从变量声明开始全用long long。2.3 读懂OJ的裁判返回结果做题做得多了你会发现OJ系统报的各种缩写比题目本身还难懂。第54题虽然简单但正好可以用来熟悉这些状态。每个状态对应一个不同的错误类型缩写全称常见原因ACAccepted完全正确WAWrong Answer结果错误多为类型、公式或边界问题PEPresentation Error输出格式不对如多空格少换行TLETime Limit Exceeded超时循环累加遇到超大n就可能出现RERuntime Error运行错误数组越界、除零等CECompile Error编译错误语法或头文件问题我在练题时见过不少人把WA当成“题目太难”其实WA往往是某个细节没处理好。比如这道题用int提交就会WA而且这种WA不是随机出现的是小数据能过、大数据崩掉最让人摸不着头脑。理解OJ返回状态还有一个好处让你学会根据返回结果反推问题。看到TLE第一反应是算法复杂度太高看到WA优先检查数据范围、输出格式和公式看到CE就去核对语法而不是反复改答案。3. 完整代码与验证流程3.1 可直接AC的参考代码下面这份代码是第54题最常规的AC写法我用C提交到东华OJ上实测可行。代码里加了注释方便对着逐行理解。#include bits/stdc.h using namespace std; int main() { long long a, b, n; // 东华OJ这类题通常是单组输入写 while 是为了兼容多组数据 while (cin a b n) { // 前 n 项和 n 个首项 公差带来的累加增量 long long sum n * a n * (n - 1) / 2 * b; cout sum endl; } return 0; }这里有两个值得展开说的地方。第一个是头文件。我用的是bits/stdc.h它是GCC编译器的万能头文件东华OJ这类在线评测系统大多用的是G编译器支持这个头文件。如果你在自己电脑上用VS Code或Visual Studio调试也基本没问题。如果碰到不支持的编译器换成#include 也是可以的。第二个是while (cin a b n)。有些同学问题目明明只给一组输入为什么用while因为while循环会在无法继续读取数据时自动结束单组测试和多组测试都能正确处理。就算题目只给一组输入while写法的结果也一样。少数同学用if (cin a b n) 也AC但习惯while会更稳妥后面遇到多组输入的题目也不用改。3.2 动手验证自己当裁判代码写完了不急着提交先在本地跑几个测试数据验证一下。这一步很多新手会跳过直接交上去结果WA了再回来改浪费时间。验证的核心方法是用手算的小数据对照。以a 1, b 2, n 4为例等差数列是1, 3, 5, 7前4项和是16。代入公式4 * 1 4 * 3 / 2 * 2 4 12 16正确。再测一个边界用例n 1时前1项和就是a本身。公式算出来是1 * a 1 * 0 / 2 * b a正确。还有一个比较隐蔽的边界b 0时序列所有项都等于a前n项和就是na。公式里后面一大块全变成0结果恰好是na也正确。把这些小数据测完之后再提交一次AC你心里的底气是完全不一样的。你会发现很多WA本质上不是“思路不对”而是“没有在提交前充分验证边界”。3.3 输入输出写法的几种选择第54题在输入输出上虽然简单但C的写法有好几种不同环境、不同习惯的适应度不一样。第一种是cin/cout配合ios::sync_with_stdio(false)加速。我的参考代码没有写加速语句因为这个题的数据量太小加不加都无所谓。但如果你以后做数据量大的题目记得在main函数开头写这两行否则cin/cout比scanf/printf慢很多。第二种是scanf/printflong long a, b, n; while (scanf(%lld%lld%lld, a, b, n) ! EOF) { long long sum n * a n * (n - 1) / 2 * b; printf(%lld\n, sum); }scanf对应long long的格式符是%lld写错成%d就会读入错误数据这也是一个常见坑点。printf输出long long同理要用%lld。第三种写法是混合使用cin读入printf输出。这种做法在竞赛里也有人用但风格不太统一我一般不推荐。要么统一用C流要么统一用C风格函数混着写容易产生格式符写错的低级错误。从学习角度我更建议新手把cin/cout先练熟。它不用记格式符可读性也好等比赛时再根据情况换scanf/printf也不迟。4. 常见错误与排查实录4.1 我在这个题上见过的三个典型错误刷题群里和带新人的过程中第54题的高频错误几乎集中在下面这三个点上我一个个说。第一个错误所有变量都用int小数据AC大数据WA。这个错法最隐蔽因为本地测试时n取10、20结果怎么算都对。提交上去一跑OJ后台用大数测试立刻WA。本质上就是2.1节讲的数据范围问题。排查方法也很简单把变量类型改成long long重新提交如果AC了说明就是类型的事。第二个错误循环边界写错。用循环求和时for (int i 0; i n; i) 和 for (int i 1; i n; i) 看着都差不多实际执行效果完全不同。前一种第一项是a 0b最后一项是a (n-1)b正确。后一种第一项是a 1b最后一项是a nb相当于把序列整体往后推了一项结果自然不对。第三个错误弄反a和b的含义。有人把b当成第二项直接用(b-a)当公差结果跟题目要求的“b是公差”产生偏差。说实话这个真不能怪代码是读题不仔细。我的建议是拿到题目后先把“a首项、b公差、n项数”这种关键定义用笔抄在草稿纸上再动手写代码。4.2 排查技巧把题目拆成最小单元遇到WA最忌讳的就是反复改公式、到处加括号像一个无头苍蝇一样乱撞。我自己排查这类基础题时习惯把题目拆成最小单元一个一个验证。比如第54题核心就是公式sum na n(n-1)/2*b。我假设它出错会按顺序检查三个独立的部分第一单独验证na的贡献。造一个b0的测试数据公差为0整个序列全是a此时sum应该等于na。如果这个用例通过了说明n*a部分没问题。第二单独验证公差部分的累积。造一个a0的测试数据首项为0序列变成0, b, 2b, ...此时sum应该等于n*(n-1)/2*b。如果这个用例通过了说明后半部分没问题。第三两部分组合起来再测一个正常数据比如a1, b2, n4手算看最终的16是否一致。这种“拆开验证”的思路其实是排查一切代码问题的通用方法。把一个大问题拆成几个小问题每个小问题都能独立验证出错的位置就无处遁形。5. 一道基础题的延伸与练习建议5.1 从54题出发的C语法清单很多同学觉得第54题学不到东西因为代码太短了。但恰恰相反这道题把C入门的几个核心知识点都串起来了我习惯把它当成一份“语法自测清单”来用。变量类型对应long long的选择输入输出对应cin/cout和scanf/printf循环对应for语句与边界控制表达式对应运算符优先级和整数除法。这些看起来零散但都是C项目的基础零件。往后学你会接触到C STL里的vector、string、sort这些工具也会开始写更长的代码。但回过头看越复杂的程序越需要扎实的基本功。很多人觉得C难难的不是STL不是那些花哨的语法糖而是连一个等差数列求和的类型和边界都处理不干净。我给新手的建议是别急着刷难题先把这类基础题做到“闭眼能写对”。你可以在自己的编辑器里新建一个文件不参考任何资料用十分钟把第54题完整写出来并运行正确。能做到这一点再往数据结构、算法进阶走地基才算稳。5.2 值得练的四个变式第54题练完我建议顺手做几个变式让同样的知识点发挥更大价值。变式一求第n项的值。公式是a (n-1)*b注意不要把n-1写错成n。这个变式训练的是对等差数列通项公式的理解。变式二已知前n项和S反推最小满足条件的n。因为前n项和随着n单调递增可以用二分查找在O(logn)时间内找到答案。这个变式把数学公式和二分法结合到了一起是竞赛入门的好练习。变式三多组输入直到文件结束。核心就是while (cin a b n)对应第54题参考代码里的写法。这是我推荐你务必掌握的输入模式因为以后很多OJ题目都这么设计。变式四题目给的是首项a1和第二项a2要你求前n项和。这时候需要用a2减去a1算出公差b再套用本题公式。这个变式增加了“数据预处理”的步骤也锻炼你灵活变通公式的能力。我自己刷题有个习惯每AC一道简单题就想想它能不能变成另一种问法然后动手写一版。这个过程比单纯提交通过更有收获因为真正让你成长的不是那个“Accepted”而是你对着屏幕改代码、验证边界的那些时间。第54题这种基础题刚好提供了这么多可以折腾的空间值得你多花半小时好好玩一玩。