算法竞赛高频题型全梳理:从刷题到模板化思维的正确进阶路线
平时带训练队和帮人改简历时被问得最多的一个问题是刷了几百道题还是没底竞赛来了照样不会做到底哪里出了问题问得多了我发现这背后其实有个共性的误解——很多人把“刷题量”当成了“能力值”但竞赛比的根本不是谁见过的题多而是谁能在有限时间内认出题目的本质、调出对应的套路。换句话说高频题型的识别能力和模板的熟练度才决定你是省奖还是国奖。这篇文章把竞赛里最常出现的题型按“入门→进阶”梯度做个完整梳理每一类都给出通用代码模板、复杂度分析、LeetCode同源题对照以及我在实战里真正踩过的坑。内容偏向那些刚打完基础题、开始做套题的选手也适合中期想系统捋一遍知识框架的人。1. 先从整体框架说起竞赛题到底在考什么很多初学者有个错觉以为竞赛题天马行空、什么怪题都有。实际上命题是有规律可循的。把每年各类比赛的题目拆开来看你会发现核心考点的出现频率极其稳定基本集中在下面这张表里。难度层级核心题型出现频率典型特征入门模拟、前缀和、双指针、二分答案极高套路固定模板直接套基础BFS、DFS、贪心、简单DP、拓扑排序高需要建模能力有标准解法进阶状态压缩DP、树形DP、最短路变形中高入门选手的区分度所在防AK线段树、数论推导、复杂图论低出现即有区分度这里的启发很明显如果你目标是稳拿铜牌或省二入门和基础部分的题必须全对进阶部分能拿一半就行。所以你看认识题型这件事本质上是帮你把有限的备赛时间花在得分效率最高的地方。我建议准备竞赛的选手做这样一件事把近三年的真题按“考点标签”而不是“题目顺序”重新整理一遍。同一考点的题放一起你很快就能发现套路是如何反复出现的。1.1 高频题型识别术拿到题先问自己三个问题拿到一道题先别急着敲代码。我习惯让队员在读题后30秒内完成三步判断——这比任何技巧都重要这道题隐藏了什么数据结构的影子比如“区间和”“区间更新”就指向线段树或前缀和“最近公共祖先”就指向LCA。数据范围允许什么复杂度n ≤ 10的五次方就不可能用O(n²)说明必须二分或O(n)扫描n ≤ 20大概率是搜索或状态压缩DP。有没有经典的“题眼”关键词比如“最大化最小值”基本就是二分答案“至少需要几次”多半是BFS最短路。第三步最关键也最容易被忽略。题眼词和算法之间的对应关系是竞赛里最值钱的经验。下面这张表是我在训练时总结的打印出来贴在电脑边也不为过。题眼关键词对应算法模板连续子数组、区间和前缀和 / 滑动窗口最大化最小值、最小化最大值二分答案 贪心检查两个数组找交集/顺序配对双指针装水、接雨水、柱状图单调栈图的最短步数、扩散次数BFS选择/不选求最优方案DP 或 DFS 剪枝两两比较、排序后配队排序 贪心别小看这个表它能帮你在赛场上省下大量犹豫时间。很多时候选手做不完题不是因为不会而是因为判断这道题用了太久。2. 入门必会五大题型这些题必须拿满分2.1 模拟题别轻敌细节才是区分度模拟题看起来谁都能做但为什么有些人AC有些人卡一整场因为模拟题的考点就是代码实现的鲁棒性。说穿了模拟题就是按照题目描述一步步做难的是边界条件的处理。比如日期计算或者矩阵旋转这些题出错的地方往往不在中间过程而在最后一步。拿矩阵顺时针旋转90度来说核心操作是先转置再左右翻转这个模板请直接背下来void rotate(vectorvectorint matrix) { int n matrix.size(); // 第一步沿对角线转置 for (int i 0; i n; i) { for (int j i; j n; j) { swap(matrix[i][j], matrix[j][i]); } } // 第二步每行左右翻转 for (int i 0; i n; i) { reverse(matrix[i].begin(), matrix[i].end()); } }模拟题的坑大多集中在循环边界、数组越界和类型溢出这三处。我的建议是任何模拟题写完先手动跑一遍样例再把n1、nmax这些极端情况各测一遍再提交。这个过程看起来浪费时间其实能帮你避免几次毫无意义的罚时。LeetCode同源题48题旋转图像54题螺旋矩阵59题螺旋矩阵II498题对角线遍历。这几道选两道练手就够了重点感受一下边界条件的处理节奏。2.2 前缀和与差分区间操作的两把快刀前缀和解决的是“多次询问区间和”的问题核心思想一句话预处理出所有前i项的和任意区间和等于两项之差。vectorint prefixSum(vectorint nums) { int n nums.size(); vectorint pre(n 1, 0); for (int i 0; i n; i) { pre[i 1] pre[i] nums[i]; } return pre; } // 查询区间 [l, r] 的和 pre[r1] - pre[l]这个模板简单到不能再简单但威力巨大。为什么因为它把O(n)的区间查询降到了O(1)。当题目里出现“m次区间查询”这类字眼时前缀和基本就是标准答案。这里我要特别讲一个容易被忽略的东西——差分。前缀和解决区间和查询差分解决区间同时加上一个数的操作。两者其实是互为逆运算的关系。差分数组d[i] a[i] - a[i-1]对区间[l,r]统一加x只需要d[l] x, d[r1] - x最后做一遍前缀和还原即可。这个技巧在竞赛里出场率极高尤其是配合排序的区间覆盖类题目。实测感受我见过不少学了一个月的选手遇到“给定n个区间操作求每个位置的值”这种题时还会老老实实写个循环逐区间更新结果数据一大就TLE。这不是能力问题是脑子里少挂了一个“区间操作”的钩子——当你看到“多次区间加”、“区间覆盖”这些词第一反应就应该是差分。LeetCode同源题303题区域和检索304题二维区域和检索1109题航班预订统计这道就是纯差分裸题370题区间加法。2.3 双指针把O(n²)降成O(n)的魔法双指针的核心思想并不复杂利用单调性让两个指针各自只朝一个方向移动从而避免重复扫描达到线性复杂度。最常见的应用场景是有序数组上的两数之和问题。之前用两层循环是O(n²)但有序数组配双指针可以将复杂度降到O(n)。vectorint twoSum(vectorint nums, int target) { int l 0, r nums.size() - 1; while (l r) { int sum nums[l] nums[r]; if (sum target) return {l, r}; else if (sum target) l; else r--; } return {}; }为什么双指针一定正确关键在于有序性带来的单调关系——左指针右移时和增大右指针左移时和减小这就保证了每一步的移动方向是唯一确定的不存在漏解。双指针还有一大变体是滑动窗口专门解决“满足某条件的最短/最长连续子数组”问题。最典型的模板是维护一个窗口右指针扩展、左指针收缩中间用一个哈希表或计数器记录窗口状态。// 找最短包含t中所有字符的子串长度 int lengthOfLongestSubstring(string s) { vectorint cnt(128, 0); int l 0, ans 0; for (int r 0; r s.size(); r) { cnt[s[r]]; while (cnt[s[r]] 1) { // 有重复就收缩 cnt[s[l]]--; l; } ans max(ans, r - l 1); } return ans; }这个模板很经典做字符串子串相关题目时可以反复用。坑提醒双指针题目最容易出错的是循环终止条件的判断。到底是l r还是l r建议每次都拿空数组、单元素数组测一下形成肌肉记忆。LeetCode同源题167题两数之和II、3题无重复字符的最长子串、76题最小覆盖子串、209题长度最小的子数组。这四题建议都做因为滑动窗口和双指针的配合场景在竞赛里出场率极高。2.4 二分答案把最优化问题变成判定问题这是我最想强调的一个模板因为它是竞赛入门选手升级的一个重要分水岭。很多题目问的是“最小化最大值”或“最大化最小值”比如“在m天内运完所有货物求最小运力”“划分成k段求各段和最大值的最小值”。这种题直接求很难但如果你反过来问给定一个答案x能不能做到就变成了一个极其简单的贪心判定——多简单呢最粗暴的写法就是按顺序扫一遍数数即可。这种思路就是二分答案。它的通用模板bool check(int x) { // 判断答案x是否可行根据题目实现 // 返回值x可行返回true否则返回false } int binarySearch(int left, int right) { while (left right) { int mid (left right) / 2; if (check(mid)) right mid; // 如果mid可行尝试更小的 else left mid 1; } return left; }模板看似简单但有两个容易错的点第一个mid的计算方式。用(left right) / 2在leftright很大会溢出建议写成left (right - left) / 2。这个细节比赛里经常见选手翻车。第二个边界更新时的左闭右开问题。模板里用的是right mid和left mid 1这是“可行域最小值”的写法但如果题目要求的是“最大值”循环条件和更新逻辑就要反过来。经验分享初学者练二分答案建议先别急着看题解而是先训练自己把原题改写成“给定x判断是否能做到”的check函数。这一步完成了剩下的就是套模板。一套二分复杂度是O(logM)其中M是答案范围check是O(n)的话总复杂度就是O(n logM)对于n ≤ 10⁵的数据范围完全够用。LeetCode同源题1011题在D天内送达包裹的能力、410题分割数组的最大值、875题爱吃香蕉的珂珂。这三题是二分答案的三胞胎做完你会彻底理解这个套路。2.5 贪心算法难在证明容易在写代码贪心的代码往往20行就搞定了但很多选手却不敢写因为怕贪错了。这里给大家一个定心丸竞赛里的贪心题大多数都带着“排序”这个前置操作。排序后贪心是最经典的套路组合。比如“给一堆会议时间选尽量多的会议参加”按结束时间排序后能选就选这就是最优解。int maxEvents(vectorvectorint events) { sort(events.begin(), events.end(), [](autoa, autob) { return a[1] b[1]; // 按结束时间升序 }); int lastEnd -1, ans 0; for (auto ev : events) { if (lastEnd ev[0]) { // 当前会议可以和上一个共存 ans; lastEnd ev[1]; } } return ans; }关于贪心证明这个问题我的观点是初学者不需要每次都会严格证明但必须有意识地用“反例测试”来验证方案。写完之后尝试构造一个能推翻自己贪心策略的例子如果找不到这个策略大概率是对的。LeetCode同源题435题无重叠区间、452题用最少数量的箭引爆气球、55题跳跃游戏、134题加油站。这些题都带着“排序贪心”的味道适合一起刷。3. 进阶必会六类题有区分度的题型都在这3.1 BFS最短路图上的扩散问题BFS的模板非常固定所有“最少步数”“最短路径”“扩散次数”类问题都能套int bfs(vectorvectorint grid, int sx, int sy) { int n grid.size(), m grid[0].size(); int dx[] {0, 0, 1, -1}; int dy[] {1, -1, 0, 0}; queuepairint, int q; vectorvectorbool vis(n, vectorbool(m, false)); q.push({sx, sy}); vis[sx][sy] true; int steps 0; while (!q.empty()) { int sz q.size(); while (sz--) { // 按层出队 auto [x, y] q.front(); q.pop(); if (满足目标条件) return steps; for (int i 0; i 4; i) { int nx x dx[i], ny y dy[i]; if (nx 0 nx n ny 0 ny m !vis[nx][ny] grid[nx][ny] ! 障碍) { vis[nx][ny] true; q.push({nx, ny}); } } } steps; } return -1; // 不可达 }这里最关键的一步是在入队时标记访问而不是出队时标记。很多新手习惯出队时标记访问结果同一个节点被重复入队多次内存和速度双双爆炸还可能死循环。这个坑我在带新人时几乎每次都会遇到。扩展思路如果边权不都为1比如有些格子走一步代价是5就用Dijkstra如果边权只有0和1用01-BFS双端队列0权重放队首1权重放队尾。这两者都是BFS的进阶版竞赛里出现的频率也不低。LeetCode同源题200题岛屿数量、279题完全平方数、994题腐烂的橘子、1162题地图分析。这些题做完BFS的骨架应该就能牢牢掌握了。3.2 树的遍历与递归二叉树模板必须烂熟于心树的题在竞赛里以中档题为主区分度主要体现在你能不能把递归逻辑写清楚。下面这个三段式是所有二叉树题的通用框架// 前序遍历 void traversal(TreeNode* root) { if (!root) return; // 终止条件 visit(root); // 访问操作 traversal(root-left); // 左子树 traversal(root-right); // 右子树 }三种遍历的唯一区别就是visit(root)这行代码所在的位置。放最前面是前序放中间是中序放最后是后序。这个框架能解决绝大多数递归树题。进阶应用当你遇到“树的最大路径和”“树的最近公共祖先”“树形DP”这些题时本质上都是在递归遍历的过程中利用返回值传递信息。比如求二叉树最大深度返回值就是子树深度加一int maxDepth(TreeNode* root) { if (!root) return 0; return 1 max(maxDepth(root-left), maxDepth(root-right)); }树形DP是竞赛中的一个重要进阶考点核心思路是把dp数组定义在树节点上通过递归回溯时合并子节点的状态。经典题目如“打家劫舍III”“树的最长直径”都属于这个范畴。LeetCode同源题104题最大深度、226题翻转二叉树、110题平衡二叉树、543题二叉树的直径、236题最近公共祖先、337题打家劫舍III经典树形DP入门题。3.3 DFS回溯当搜素状态空间不大时这就是暴力美学DFS回溯本质上是穷举所有可能路径只是在走不通时提前返回剪枝避免无效搜索。大多数用DFS的题目可以统一成这样一个模板void dfs(当前状态, 路径记录) { if (满足结束条件) { 保存一个可行解; return; } for (每个可能的下一步选择) { if (选择不合法) continue; 做选择; dfs(更新后的状态, 更新后的路径); 撤销选择; // 回溯 } }回溯最难理解的点在于“撤销选择”这一步也就是为什么递归结束之后要把状态改回去。这个机制可以这样理解所有递归分支共享同一份状态数组如果不撤销上一条分支的修改就会“污染”下一条分支的计算结果。看一个全排列的经典例子void dfs(vectorint nums, vectorbool used, vectorint path, vectorvectorint ans) { if (path.size() nums.size()) { ans.push_back(path); return; } for (int i 0; i nums.size(); i) { if (used[i]) continue; used[i] true; path.push_back(nums[i]); dfs(nums, used, path, ans); path.pop_back(); used[i] false; } }重要提醒DFS题目最关键的是评估状态空间的大小。如果状态数在百万级别以内DFS随便跑如果超过千万就必须上剪枝或换算法了。盲目写DFS而忽略状态空间是很多选手TLE的根本原因。LeetCode同源题46题全排列、78题子集、39题组合总和、79题单词搜索、51题N皇后。前四题是最经典的“组合模板”建议至少做两遍。3.4 动态规划入门状态定义这一步做对了题目就对了一半DP难不在转移方程难在状态定义。很多人卡住是因为一开始状态就定义歪了。两件事先搞清楚dp[i]代表什么从dp[j]怎么推出dp[i]最经典的上楼梯dp[i] dp[i-1] dp[i-2]表示到第i级的方法数等于到i-1和i-2级的方法数之和。这个例子看起来简单但它揭示了DP的本质当前状态由之前的某些状态转移而来。所有DP都用这个模板vectorint dp(n, 0); dp[0] 初始值; // 初始化边界 for (int i 1; i n; i) { for (int j 0; j i; j) { // 枚举可转移的前驱状态 dp[i] max(dp[i], dp[j] 转移代价); } } return dp[n-1];入门阶段先练两类DP就足够第一种线性DP。特点是状态数组是一维的转移依赖前面某个位置的状态。典型题目如最大子序和、最长递增子序列——后者是竞赛中的“常青树”。int lengthOfLIS(vectorint nums) { int n nums.size(), ans 1; vectorint dp(n, 1); // dp[i]表示以nums[i]结尾的最长递增子序列长度 for (int i 1; i n; i) { for (int j 0; j i; j) { if (nums[j] nums[i]) { dp[i] max(dp[i], dp[j] 1); ans max(ans, dp[i]); } } } return ans; }第二种背包DP。0-1背包和完全背包是竞赛的绝对热门模板务必背熟// 0-1背包每个物品只能用一次 for (int i 1; i n; i) { for (int w capacity; w weight[i]; w--) { dp[w] max(dp[w], dp[w - weight[i]] value[i]); } } // 完全背包每个物品不限次数 for (int i 1; i n; i) { for (int w weight[i]; w capacity; w) { dp[w] max(dp[w], dp[w - weight[i]] value[i]); } }有个细节值得注意0-1背包的内层循环是逆序的完全背包是正序的。原因很简单——逆序防止一个物品被重复选取正序则允许重复选取。这个细微差别背后是两种完全不同的算法语义理解了这一点背包题基本就通了。LeetCode同源题70题爬楼梯、198题打家劫舍、300题最长递增子序列、322题零钱兑换、416题分割等和子集0-1背包裸题、518题零钱兑换II完全背包裸题。3.5 拓扑排序有依赖关系的顺序问题当题目中出现“先修课”“依赖关系”“先后顺序”这样的字眼时基本可以锁定拓扑排序。拓扑排序的核心思想很朴素不断找出当前入度为0的节点把它放进结果序列然后去掉它所有出边即减少邻居入度。如果最终结果节点数不等于总节点数说明图中存在环问题无解。vectorint topoSort(int n, vectorvectorint edges) { vectorvectorint g(n); vectorint inDeg(n, 0); for (auto e : edges) { g[e[0]].push_back(e[1]); inDeg[e[1]]; } queueint q; for (int i 0; i n; i) { if (inDeg[i] 0) q.push(i); } vectorint ans; while (!q.empty()) { int u q.front(); q.pop(); ans.push_back(u); for (int v : g[u]) { if (--inDeg[v] 0) q.push(v); } } if (ans.size() ! n) return {}; // 存在环 return ans; }这个算法有个看似微小但很重要的点入队的节点顺序。如果要求字典序最小的拓扑序把queue换成priority_queue即可。很多题目在这一点上做文章所以模板里建议把容器位置留出来视题目要求替换。LeetCode同源题207题课程表、210题课程表II、802题找到最终的安全状态。这三题做完拓扑排序就能应付绝大多数情况了。3.6 最短路算法Dijkstra模板及优化图论题里最短路是重中之重竞赛中的常见场景是“带权图求最少代价路径”。如果所有边权非负就用Dijkstra存在负权边用SPFA或Bellman-Ford。竞赛里前者出现的次数远超后者。Dijkstra 优先队列堆优化的模板是目前的主流写法复杂度O(E log V)对大多数数据范围够用vectorint dijkstra(int n, vectorvectorpairint,int g, int s) { vectorint dist(n, INT_MAX); dist[s] 0; priority_queuepairint,int, vectorpairint,int, greater pq; pq.push({0, s}); while (!pq.empty()) { auto [d, u] pq.top(); pq.pop(); if (d dist[u]) continue; // 跳过旧最短路径 for (auto [v, w] : g[u]) { if (dist[u] w dist[v]) { dist[v] dist[u] w; pq.push({dist[v], v}); } } } return dist; }这里有一个值得说明的细节if (d dist[u]) continue这个判断本质上是一次懒删除跳过已经过时的队列记录。没有这行代码算法虽然慢点但结果依然正确有这行代码性能提升非常明显。很多初学者不理解为何已经入队过的节点还会重复入队因为有更新的dist[v]更新时就会再push一次所以同一个节点可能在队列里出现多次配合优先队列只会优先处理最短的那个。实际教训在带新人的过程中我发现最短路题最常出的错不是Dijkstra本身而是建图时忽略重边和自环。题目没说没有重边你不处理就会得到错误答案。建图时直接取最小值即可这个习惯请尽早养成。LeetCode同源题743题网络延迟时间纯Dijkstra裸题、1514题概率最大的路径乘法版最短路、1631题最小体力消耗路径可用二分Dijkstra进阶题。4. 高频考点的进阶变体走到这一步已经有了拿牌实力4.1 状态压缩DP小数据范围的暴力最优解当题目数据范围中出现n ≤ 20这种小而明确的限制时大概率就是状态压缩DP的信号。核心思路用一个整数的二进制位表示一个集合的状态dp[mask]表示处于该状态时的最优值。比如经典的旅行商问题dp[mask][i]表示已经访问过的城市集合为mask当前在城市i时的最短路径int dp[1n][n]; for (int mask 1; mask (1n); mask) { for (int i 0; i n; i) { if (!(mask (1i))) continue; // 当前不包含城市i // 从上一个包含城市j的状态转移过来 } }这类题的关键在于用位运算快速判断状态转移的合法性。常用的几个位运算要滚瓜烂熟判断第i位是否为1用mask (1i)设置第i位为1用mask | (1i)去掉第i位用mask ^ (1i)枚举mask的所有非空子集用sub (sub-1) mask。LeetCode同源题526题优美的排列、691题贴纸拼词、847题访问所有节点的最短路径。这三题由简到难第一题比较适合作为状态压缩的入门体验。4.2 线段树进阶区间操作的最强通用解线段树是竞赛中的重量级选手能处理所有区间类问题——区间求和、区间最大值、区间加、区间赋值、区间合并等等。它本质上是一棵完全二叉树每个节点对应一段区间通过递归完成操作。线段树的代码量比之前所有模板都大但这套模板值得熟记。一个支持“区间加 区间求和”的简化版struct SegTree { vectorlong long sum, lazy; vectorint nums; int n; SegTree(vectorint arr) { n arr.size(); nums arr; sum.resize(4*n); lazy.resize(4*n); build(1, 0, n-1); } void build(int node, int l, int r) { if (l r) { sum[node] nums[l]; return; } int mid (lr)1; build(node1, l, mid); build(node1|1, mid1, r); sum[node] sum[node1] sum[node1|1]; } void pushDown(int node, int l, int r) { if (lazy[node] 0) return; int mid (lr)1; int leftChild node1, rightChild node1|1; lazy[leftChild] lazy[node]; lazy[rightChild] lazy[node]; sum[leftChild] lazy[node] * (mid - l 1); sum[rightChild] lazy[node] * (r - mid); lazy[node] 0; } void update(int node, int l, int r, int L, int R, long long val) { if (L l r R) { lazy[node] val; sum[node] val * (r - l 1); return; } pushDown(node, l, r); int mid (lr)1; if (L mid) update(node1, l, mid, L, R, val); if (R mid) update(node1|1, mid1, r, L, R, val); sum[node] sum[node1] sum[node1|1]; } long long query(int node, int l, int r, int L, int R) { if (L l r R) return sum[node]; pushDown(node, l, r); int mid (lr)1; long long res 0; if (L mid) res query(node1, l, mid, L, R); if (R mid) res query(node1|1, mid1, r, L, R); return res; } };这里的pushDown是懒标记的核心更新整个覆盖区间时不往下层层修改而是先记住这个节点的延期修改等真正需要访问子节点时才把修改传递下去。这个技巧保证了每次区间操作的复杂度是O(log n)而非O(n)。建议线段树是很多进阶题的“底层工具”但没必要一开始就花一周死磕。我的训练顺序是先把上一节的前缀和、差分、树状数组练熟再上线段树这样理解起来会顺畅得多。LeetCode同源题307题区域和检索线段树入门、699题掉落的方块离散化线段树偏难、1157题子数组中占绝大多数的元素线段树随机化。4.3 并查集动态连通性问题的最佳解法并查集解决的是“判断两个元素是否连通”的问题配合路径压缩和按秩合并均摊复杂度几乎O(1)。为什么并查集在竞赛里高频出现因为它把“无向图连通性判断”这一问题从反复DFS的O(nm)降到了接近线性的水平。struct DSU { vectorint fa, sz; DSU(int n) { fa.resize(n); sz.resize(n, 1); iota(fa.begin(), fa.end(), 0); // 初始时每个元素自成一族 } int find(int x) { if (fa[x] ! x) fa[x] find(fa[x]); // 路径压缩 return fa[x]; } void merge(int a, int b) { a find(a); b find(b); if (a b) return; if (sz[a] sz[b]) swap(a, b); // 按大小合并 fa[b] a; sz[a] sz[b]; } };实操心得路径压缩用递归写简洁但极端情况下如下面节点特别深有爆栈风险。如果你实现的环境对递归深度有严格限制可以用迭代版本——先循环找出根节点再沿路径把每个节点的fa直接指向根。我一直建议选手两种写法都掌握平时用递归特殊场合用迭代。并查集的一些高级玩法还包括带权并查集在合并时维护节点到根的权值差用于处理元素之间相对关系的题目和可撤销并查集记录每一步操作支持回退后者配合离线分治能解决一类较难的区间查询问题。这部分如果时间充裕值得学一学。LeetCode同源题547题省份数量、684题冗余连接、721题账户合并、990题等式方程的可满足性。这四题覆盖了并查集的大部分基础用法且难度平滑上升。5. 通用模板与刷题路线怎么练才能形成肌肉记忆5.1 一套通用的竞赛代码模板熟悉了各个题型以后应该沉淀出一套自己的代码习惯。下面这份是我个人长期使用的C竞赛开局模板可以根据所需自行调整#include bits/stdc.h using namespace std; typedef long long LL; typedef pairint, int PII; // 快速读入输出重要能省不少时间 inline int readInt() { int x0, f1; char cgetchar(); while(c0||c9) { if(c-) f-1; cgetchar(); } while(c0c9) { x(x1)(x3)(c^48); cgetchar(); } return x*f; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); // 解题代码 return 0; }bits/stdc.h编译速度在本地可能慢一点但各大竞赛平台都支持所以这个模板在赛场上直接可用。ios::sync_with_stdio(false); cin.tie(nullptr);这两行也是必写的否则cin/cout在某些题目上会比scanf/printf慢很多遇到大数据量的输入直接拖垮全题。5.2 按题型的刷题路线三轮学习法把上面的模板和题型用起来我推荐“三轮刷题法”第一轮看懂模板。把本文列出的每个模板抄一遍一行一行读懂自己跑通一个简单测试样例。这个阶段不用求快重在理解每一行的意义。第二轮配套刷题。每类题型配合3-5道LeetCode同源题先自己做卡住了再看模板对应位置。做完这轮应该能做到只花一两分钟就能想起来题型对应的核心思路。第三轮限时套卷。找一套近年的竞赛真题按比赛时间限制完整做一遍。这一步的目的不是刷题而是训练时间分配哪些题该拿多少时间、做不出来时什么时候该放弃去保下一题。刷题量到底要多少才够保奖我给一个参考入门阶段把每种题型刷满15题核心题库500题左右这个量对铜牌来说基本足够。再往上量的边际效用就下降了更多靠思维训练和套卷模拟经验。5.3 训练中的常见思维误区带新人的过程中有三类误区出现频率最高写了这么多模板最后集中说说。第一个误区是“模板背下来了但没理解适用范围”。比如有人会拿双指针去处理未排序数组的两数之和得到错误答案。模板始终是有限条件下成立的使用前必须确认前提条件是否满足。第二个误区是“算法复杂度分析不到位”。判断一道题能不能用O(n²)的算法不只看n的大小还要看时间限制和常数因子。同一道题在竞赛平台和LeetCode上的测评压力完全不同习惯LeetCode宽松评测的选手在正规赛场上很容易超时。第三个误区最隐蔽却影响最大拿到新题先想“这是什么类型”。但很多题并不是纯种的单一类型而是几种题型的叠加组合。比如“树的直径”既需要树的遍历基础又需要两次BFS或树形DP“接雨水”既是双指针也是单调栈。我建议在掌握了单一模板之后有意识地去练这些“缝合怪”题目这才是真正的能力提升点所在。6. 最后的经验总结关于比赛节奏与心态的几条建议很多技术上的东西说完了但真正上赛场时会发现技术之外的细节同样决定成败。这里说几条我自己实际比赛和带队总结出的心得。第一时间分配比刷题量更重要。我个人的比赛策略是开赛后先花15分钟把六道题全部读完给每道题标注预估难度和大致算法方向。然后按“做出一道签到题→冲击最有把握的中档题→再回头处理难题”的顺序推进。把最容易拿分的题留在最后做是最亏的时间安排。第二罚时是隐形的排名杀手。竞赛里提交错误一次会有罚时加成这意味着你提交一个不AC的代码实际代价比你以为的更大。所以提交前务必自己多测几组数据。现场赛不像LeetCode有多次提交机会这个问题更加尖锐。第三模板只是基础现场才是真正综合能力的检验。建议在赛前几天不依赖模板库里查注释闭卷默写一遍最常用的几个模板——线段树、Dijkstra、拓扑排序、最长递增子序列。能默写出来才算真正内化。不能默写出来就说明还停留在“看懂”的层面离“随时调用”还有距离。竞赛这条路的吸引力正在于它让你反复体验到“原来这个看似复杂的问题拆解之后不过就是一个已知模板一点组合变形”。希望这篇题型总结能帮你在赛前把知识框架理清楚找到自己最值得突破的那个方向。

相关新闻

基于数据分析的光伏板积灰检测与清洗策略优化

基于数据分析的光伏板积灰检测与清洗策略优化

简介:这份资源面向光伏电站运维技术人员、数据分析与数学建模方向的高校师生,围绕光伏板积灰导致的发电效率下降问题,提供一套从数据清洗到清洗策略优化的完整建模方案。内容涵盖对4个电站发电量、辐照及气象数据的预处理,按小时整…

2026/10/17 14:52:34 阅读更多 →
3B开源GUI模型本地部署实战:小显存跑AI操作电脑助手

3B开源GUI模型本地部署实战:小显存跑AI操作电脑助手

最近几天,一个来自某高校开源团队的3B参数GUI交互模型在开发者圈子里讨论度很高。社区晒出的评测数据很有意思:在同一条GUI操作基准上,它把某闭源旗舰大模型压在了身后。我第一时间把权重下载下来,又翻出一台只有8G显存的旧工作站…

2026/10/17 14:52:34 阅读更多 →
水星工业相机开发实战:从SDK配置到稳定采集的完整指南

水星工业相机开发实战:从SDK配置到稳定采集的完整指南

接到这个开发任务的时候,我其实有点懵——对方只丢给我一台水星系列工业相机,说“你把它接入系统,能采图就行”。等我把相机真正跑起来,才发现工业相机和消费级摄像头完全是两个物种。这个系列因体积小、接口丰富,在很…

2026/10/17 14:52:34 阅读更多 →

最新新闻

Flutter智能对话助手实战:Provider状态管理与DeepSeek接入解析

Flutter智能对话助手实战:Provider状态管理与DeepSeek接入解析

前阵子在做一个 Flutter 侧的智能对话小助手,需求表面上看很清爽:界面一个对话框,用户输入问题,客户端把消息发给大模型接口,拿到回复展示出来,另外要处理好加载中、失败重试、历史记录这些状态。真正动手才…

2026/10/17 15:44:10 阅读更多 →
要素数据到基础地理实体:转换技术规程关键要点与工程实践

要素数据到基础地理实体:转换技术规程关键要点与工程实践

简介:面向基础地理信息专业人员与技术管理人员,这份技术规程围绕1∶500、1∶1 000、1∶2 000比例尺基础地理信息要素数据向基础地理实体数据的转换编制,服务于新型基础测绘与实景三维中国建设,也可为城市规划、建设和管理提供数据…

2026/10/17 15:44:10 阅读更多 →
基于Spark的电影推荐系统:从协同过滤到实时推荐的全链路解析

基于Spark的电影推荐系统:从协同过滤到实时推荐的全链路解析

简介:这份资源是面向大数据与推荐系统方向学习者、毕业设计选题学生的完整项目资料,围绕Spark技术栈构建电影推荐系统,解决海量影片中个性化筛选与精准推荐的问题。内容涵盖基于人口统计学、基于内容及协同过滤三类推荐算法的原理与设计&…

2026/10/17 15:44:10 阅读更多 →
计算机博弈核心原理:从博弈树到剪枝算法解析

计算机博弈核心原理:从博弈树到剪枝算法解析

简介:这份计算机博弈竞赛辅导PPT由东北大学机器博弈研究室整理,面向准备参加计算机博弈类竞赛的学生、教练及入门研究者,系统讲解从理论基础到实战评估的核心内容。课件围绕棋类介绍与分类、计算机博弈基本原理、博弈树展开与分析、棋局评估、…

2026/10/17 15:44:10 阅读更多 →
Impala工程设计本质:低延迟交互式分析的物理约束与取舍

Impala工程设计本质:低延迟交互式分析的物理约束与取舍

1. 这不是普通读书笔记:Impala论文背后的真实工程逻辑“impala paper笔记”这五个字,最近在数据平台、大数据架构和实时分析相关的技术圈里频繁出现。但如果你真去翻那篇2012年发表在VLDB上的《Impala: A Modern Analytic Database for Hadoop》&#xf…

2026/10/17 15:44:10 阅读更多 →
双串锂电主动均衡设计:MP2642GR-Z与ATmega324P工程实践

双串锂电主动均衡设计:MP2642GR-Z与ATmega324P工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 15:43:10 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →