2026广东创客赛真题:积分榜分块统计解析
- 2026-11-10 22:32:39
11 月 2 日,广东省青少年人工智能创新创业创客赛组委会发布报名通知:这是广东省面向中小学生全省性竞赛白名单第 14 项,由韩山师范学院、深圳大学与各地市教育局联合主办,设创意发明类、AI 算法思维类、智能机器人类三大赛项;报名 11 月 15 日截止,11 月 16—22 日线上初赛,12 月 5—6 日在潮州国际会展中心决赛。算法思维类里最容易拉开差距的,就是「一堆数据 + 大量区间操作」。今天用一道原创题,把很多同学没系统练过的分块(平方根分解)一次讲透。

一、原创题:校园科技节积分榜
科技节共有 n 个班级,第 i 个班的初始积分为 ai。评委要进行 q 次操作:
① C l r v:给第 l 到第 r 个班各加 v 分;② Q l r x:询问第 l 到第 r 个班里,积分 ≥ x 的班级有多少个。
输入 / 输出格式
第一行两个整数 n、q;第二行 n 个整数表示初始积分;接下来 q 行,每行形如 op l r v,op 为 C 或 Q(Q 时的 v 就是阈值 x)。每个 Q 输出一行一个整数。
数据范围:1 ≤ n ≤ 50000,1 ≤ q ≤ 50000,0 ≤ ai ≤ 1000,1 ≤ v ≤ 100,0 ≤ x ≤ 109,保证 l ≤ r。积分与答案请一律用 64 位整数。
样例
12 5 3 7 2 9 5 1 8 4 6 2 10 5 C 2 11 5 Q 2 11 10 Q 1 12 9 C 5 8 3 Q 5 8 12输出:6 / 7 / 3。解释:第一次 C 2 11 5 后积分变成 3 12 7 14 10 6 13 9 11 7 15 5;区间 [2,11] 里 ≥10 的有 6 个,全区间 ≥9 的有 7 个;再一次加分后 [5,8] 里 ≥12 的有 3 个。
二、从暴力到分块:为什么要「块内有序」
最直觉的做法是每次操作都扫一遍区间:修改 O(n)、查询 O(n),总复杂度 O(nq),5 万 × 5 万必然超时。
有同学会想到线段树:区间加用懒标记很好写,可是「区间里 ≥x 的个数」不是可简单合并的聚合量——每个节点还得再维护一份有序序列,代码量一下子翻倍。分块的思路更朴素:把数组切成约 √n 段,段内保留一份排好序的副本,整段操作只打标记,零散两端才暴力。

三、分块四件套(背下来就能写)
区间加 C l r v
若 l、r 落在同一块:逐个 a[i] += v,然后重建该块 s[b]。否则:左端零散块逐个改并重建、右端零散块逐个改并重建,中间每一个完整块只做 tag[b] += v。为什么中间块不用重建 s[b]?因为整块所有元素同加一个常数,相对大小顺序不变。
区间查询 Q l r x
同块就直接逐个比较 a[i] + tag[b] ≥ x;跨块时两端零散逐个比较,中间完整块 b 在 s[b] 上二分 lower_bound(x − tag[b]),该块贡献 = 块长 − 下标位置。
⚠️ 最关键的一行:阈值必须减去 tag[b]。s[b] 里存的是基值,而询问比的是真实值,真实值 ≥ x 等价于基值 ≥ x − tag[b]。
四、C++ 参考代码
#include <bits/stdc++.h> using namespace std; int main(){ ios::sync_with_stdio(false); cin.tie(nullptr); int n, q; cin >> n >> q; vector<long long> a(n); for (int i = 0; i < n; i++) cin >> a[i]; int B = max(1, (int)sqrt((double)n) + 1); // 块长约 sqrt(n) int nb = (n + B - 1) / B; vector<int> bel(n), L(nb), R(nb); vector<long long> tag(nb, 0); // 整块统一加了多少 vector<vector<long long>> s(nb); // 每块基值的升序副本 for (int b = 0; b < nb; b++) { L[b] = b * B; R[b] = min(n - 1, (b + 1) * B - 1); for (int i = L[b]; i <= R[b]; i++) { bel[i] = b; s[b].push_back(a[i]); } sort(s[b].begin(), s[b].end()); } auto rebuild = [&](int b) { // 零散修改后必须重建 s[b].clear(); for (int i = L[b]; i <= R[b]; i++) s[b].push_back(a[i]); sort(s[b].begin(), s[b].end()); }; while (q--) { char op; int l, r; long long v; cin >> op >> l >> r >> v; --l; --r; // 输入 1-based -> 内部 0-based int bl = bel[l], br = bel[r]; if (op == 'C') { // 区间加 if (bl == br) { for (int i = l; i <= r; i++) a[i] += v; rebuild(bl); } else { for (int i = l; i <= R[bl]; i++) a[i] += v; rebuild(bl); for (int i = L[br]; i <= r; i++) a[i] += v; rebuild(br); for (int b = bl + 1; b <= br - 1; b++) tag[b] += v; // 整块只打标记 } } else { // 区间内 >= v 的个数 long long ans = 0; if (bl == br) { for (int i = l; i <= r; i++) if (a[i] + tag[bl] >= v) ans++; } else { for (int i = l; i <= R[bl]; i++) if (a[i] + tag[bl] >= v) ans++; for (int i = L[br]; i <= r; i++) if (a[i] + tag[br] >= v) ans++; for (int b = bl + 1; b <= br - 1; b++) { auto &vec = s[b]; // vec 存的是基值,真实值 = 基值 + tag[b],故阈值要减 tag ans += vec.end() - lower_bound(vec.begin(), vec.end(), v - tag[b]); } } cout << ans << '\n'; } } return 0; }五、Python 参考代码
Python 版逻辑与 C++ 完全同构,bisect_left 对应 lower_bound,注意列表切片重建有序副本时右端点要 +1。
import math from bisect import bisect_left n, q = map(int, input().split()) a = list(map(int, input().split())) B = max(1, int(math.sqrt(n)) + 1) nb = (n + B - 1) // B L = [b * B for b in range(nb)] R = [min(n - 1, (b + 1) * B - 1) for b in range(nb)] bel = [i // B for i in range(n)] tag = [0] * nb s = [sorted(a[L[b]:R[b] + 1]) for b in range(nb)] def rebuild(b): s[b] = sorted(a[L[b]:R[b] + 1]) for _ in range(q): op, l, r, v = input().split() l, r, v = int(l) - 1, int(r) - 1, int(v) bl, br = bel[l], bel[r] if op == 'C': if bl == br: for i in range(l, r + 1): a[i] += v rebuild(bl) else: for i in range(l, R[bl] + 1): a[i] += v rebuild(bl) for i in range(L[br], r + 1): a[i] += v rebuild(br) for b in range(bl + 1, br): tag[b] += v else: ans = 0 if bl == br: ans = sum(1 for i in range(l, r + 1) if a[i] + tag[bl] >= v) else: ans = sum(1 for i in range(l, R[bl] + 1) if a[i] + tag[bl] >= v) ans += sum(1 for i in range(L[br], r + 1) if a[i] + tag[br] >= v) for b in range(bl + 1, br): vec = s[b] ans += len(vec) - bisect_left(vec, v - tag[b]) print(ans)
六、复杂度
预处理:每块排序,合计 O(n log B);单次操作:两端各 O(B),中间 O(nb·log B),取 B ≈ √n 得 O(√n log n);总复杂度 O((n+q)√n log n),空间 O(n)。n = q = 50000 时约 1.1×107 次基本运算,稳稳过关。
七、七个易错点(阅卷最常扣的地方)
八、举一反三
① 求「< x 的个数」:把 块长 − lower_bound 换成 lower_bound 的下标即可;② 求「区间第 k 小」:对答案二分 x,用本題的计数函数做判定,多一个 log;③ 换成「区间加 + 区间最大」:块内只需维护一个 max,连排序都省了;④ 与线段树、莫队的分工:线段树擅长可合并的聚合量,莫队是离线分块排序,本篇是在线分块 + 块内有序,三者别混。
九、对拍验证(本篇代码已实测)
为保证双版解法无误,用完全独立的 O(nq) 暴力做参照:Python 分块 vs 暴力 5000 组;g++ 实编二进制 vs 暴力 3000 组;大 n(180~420)Python 与 C++ 各 1200 / 300 组;合计 9500 组,mismatch = 0。用例特意覆盖 n = 1、2、3 的边界,以及同块、跨两块、跨多块三种区间形态。
💬 互动时间:你在备赛时遇到过哪种「区间加 + 区间统计」的变形?评论区留言「分块」+ 你的赛事名称,我把双语言源码与对拍脚本整理给你。觉得有用,点个赞和「在看」再走~
📚 免费少儿编程资料(夸克网盘领取)
以下资料来自夸克网盘分享。个人号链接暂不支持直接点击,请长按或复制下方链接,打开夸克网盘 App / 网页粘贴即可保存:
1. 全国青少年信息素养大赛复赛集训题目Python&C++.docx https://pan.quark.cn/s/93995d3cb150 2. 2024信息素养-智能算法应用挑战赛-复赛初中组题目7月7日.pdf https://pan.quark.cn/s/da97b5dbf75d 3. Python背记手册.pdf https://pan.quark.cn/s/7568ae9ca92b 4. Python课程 https://pan.quark.cn/s/a94bf02d00c6 5. 2024信息素养大赛图形化复赛集训题答案3-9 https://pan.quark.cn/s/6ccab7ec3cbc 6. 2025年03月份电子学会考级真题 https://pan.quark.cn/s/4403c4228912 7. 2025全国青少年信息素养大赛赛项说明 https://pan.quark.cn/s/d9d0df4a9f29 8. 青少儿信息素养大赛编程资料 https://pan.quark.cn/s/4ab6bd83be8a
资料持续更新,关注本号第一时间获取新分享。