【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习)

四季读书网 1 0
【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习)
【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第1张科普类资源清单【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第2张
信奥业余科普

【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第3张【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第4张GESP学习资源清单【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第5张【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第6张

真题
练习题
考纲解析
一级真题解析一级练习题清单【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第7张111-8级全考纲解析
二级真题解析二级练习题清单GESP/CSP必备技能
三级真题解析三级练习题清单考纲解密
四级真题解析四级练习题清单资源汇总/经验交流
五级真题解析五级练习题清单
六级真题解析六级练习题清单


【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第8张【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第9张CSP学习资源清单【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第10张【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第11张

CSP-XL
CSP-J
2025辽宁CSP-XL复赛真题解析CSP-J 编程题真题解析
CSP-J 客观题真题解析

【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第12张【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第13张NOI学习资源清单【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第14张【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第15张
NOIP
1998年真题解析‍‍
1999年真题解析
2001年真题解析‍‍
2011年真题解析

CSP-J 2021真题-插入排序,模拟与排序考点,重点考察对插入排序稳定性的理解以及增量更新排名的优化能力,适合GESP四-六级及以上考生练习,难度⭐⭐⭐,洛谷难度等级普及/提高−

P7910 [CSP-J 2021] 插入排序

题目要求

题目描述

插入排序是一种非常常见且简单的排序算法。小 Z 是一名大一的新生,今天 H 老师刚刚在上课的时候讲了插入排序算法。

假设比较两个元素的时间为 ,则插入排序可以以  的时间复杂度完成长度为  的数组的排序。不妨假设这  个数字分别存储在  之中,则如下伪代码给出了插入排序算法的一种最简单的实现方式:

这下面是 C/C++ 的示范代码:

for (int i = 1; i <= n; i++)for (int j = i; j >= 2; j--)if (a[j] < a[j-1]) {int t = a[j-1];   a[j-1] = a[j];   a[j] = t;  }

这下面是 Pascal 的示范代码:

for i:=1 to n do	for j:=i downto 2 do		if a[j]<a[j-1] then			begin				t:=a[i];				a[i]:=a[j];				a[j]:=t;			end;

为了帮助小 Z 更好的理解插入排序,小 Z 的老师 H 老师留下了这么一道家庭作业:

H 老师给了一个长度为  的数组 ,数组下标从  开始,并且数组中的所有元素均为非负整数。小 Z 需要支持在数组  上的  次操作,操作共两种,参数分别如下:

:这是第一种操作,会将  的第  个元素,也就是  的值,修改为 。保证 注意这种操作会改变数组的元素,修改得到的数组会被保留,也会影响后续的操作

:这是第二种操作,假设 H 老师按照上面的伪代码对  数组进行排序,你需要告诉 H 老师原来  的第  个元素,也就是 ,在排序后的新数组所处的位置。保证 注意这种操作不会改变数组的元素,排序后的数组不会被保留,也不会影响后续的操作

H 老师不喜欢过多的修改,所以他保证类型  的操作次数不超过 

小 Z 没有学过计算机竞赛,因此小 Z 并不会做这道题。他找到了你来帮助他解决这个问题。

输入格式

第一行,包含两个正整数 ,表示数组长度和操作次数。

第二行,包含  个空格分隔的非负整数,其中第  个非负整数表示 

接下来  行,每行  个正整数,表示一次操作,操作格式见【题目描述】。

输出格式

对于每一次类型为  的询问,输出一行一个正整数表示答案。

输入输出样例 #1

输入 #1
3 43 2 12 31 3 22 22 3
输出 #1
112

说明/提示

【样例解释 #1

在修改操作之前,假设 H 老师进行了一次插入排序,则原序列的三个元素在排序结束后所处的位置分别是 

在修改操作之后,假设 H 老师进行了一次插入排序,则原序列的三个元素在排序结束后所处的位置分别是 

注意虽然此时 ,但是我们不能将其视为相同的元素

【样例 #2

见附件中的 sort/sort2.in 与 sort/sort2.ans

该测试点数据范围同测试点 

【样例 #3

见附件中的 sort/sort3.in 与 sort/sort3.ans

该测试点数据范围同测试点 

【样例 #4

见附件中的 sort/sort4.in 与 sort/sort4.ans

该测试点数据范围同测试点 

【数据范围】

对于所有测试数据,满足 

对于所有测试数据,保证在所有  次操作中,至多有  次操作属于类型一。

各测试点的附加限制及分值如下表所示。

测试点
特殊性质
保证所有输入的  互不相同
保证所有输入的  互不相同

附件:sort.zip(详见洛谷网站对应题目页)


题目分析

本题基于插入排序算法构造了一个带修改的排名查询问题。需要在理解插入排序行为的基础上,设计一套高效的排名预计算与增量更新机制。

1. 理解插入排序的稳定性

观察伪代码中的交换条件 if (a[j] < a[j-1]),只在严格小于时才交换,值相等时不交换。这是稳定排序的特征:值相同的元素保持原始下标的先后顺序。因此,排序后元素的位置完全由排序关键字  决定——先按值升序,值相同时按原始下标升序。

2. 将排名转化为计数

元素  排序后的位置等于"排在它前面的元素个数" 。元素  排在  前面,当且仅当 ,或  且 。公式表示为:

3. 预计算初始排名

读入数组后,对每个元素遍历整个数组统计排在它前面的元素数量,即可得到初始排名数组。时间复杂度 ,对于  完全可以接受。

4. 修改操作的增量更新

执行操作 1 x v 时,只有  的值发生了变化。对其他元素  的排名影响是局部的——只取决于  和  之间的相对排序关系是否因修改而改变。对于每个 ,比较修改前后  是否排在  前面:

  • 如果  从"排在  前面"变为"不在前面": 减 
  • 如果  从"不在  前面"变为"排在前面": 加 

最后单独重新计算  本身。每次修改操作时间复杂度 ,不能对所有元素都用  的完整重算方式(那样每次修改将变成 ,总计 ,会超时)。

5. 查询与总体复杂度

排名数组始终维护最新状态,查询操作只需  直接输出。总体时间复杂度:初始化  + 修改  + 查询 ,约为 ,完全在题目限制内。


示例代码

#include<iostream>intmain(){    int n, Q;    std::cin >> n >> Q;    int a[8001];       // 原始数组,下标从 1 开始    int rnk[8001];     // rnk[i] 表示 a[i] 在插入排序后的位置(排名)    // 读入初始数组    for (int i = 1; i <= n; i++) {        std::cin >> a[i];    }    // 预计算初始排名    // rnk[i] = 1 + 所有"排在 i 前面"的元素个数    // 元素 j 排在 i 前面的条件:a[j] < a[i],或 a[j] == a[i] 且 j < i    for (int i = 1; i <= n; i++) {        rnk[i] = 1;        for (int j = 1; j <= n; j++) {            if (j == i) {                continue;            }            if (a[j] < a[i] || (a[j] == a[i] && j < i)) {                rnk[i]++;            }        }    }    // 处理 Q 次操作    while (Q--) {        int op;        std::cin >> op;        if (op == 1) {            // 修改操作:将 a[x] 改为 v            int x, v;            std::cin >> x >> v;            int old_val = a[x]; // 记录修改前的旧值            a[x] = v;           // 执行修改            // 增量更新其他元素的排名            // 逐一检查每个元素 i 与 x 的相对顺序是否因修改而改变            for (int i = 1; i <= n; i++) {                if (i == x) {                    continue;                }                // 修改前,x 是否排在 i 前面?                bool old_x_before_i = (old_val < a[i]) || (old_val == a[i] && x < i);                // 修改后,x 是否排在 i 前面?                bool new_x_before_i = (v < a[i]) || (v == a[i] && x < i);                if (old_x_before_i && !new_x_before_i) {                    // x 原来排在 i 前面,现在不再排在前面                    // i 前面少了一个元素,排名前移 1 位                    rnk[i]--;                } else if (!old_x_before_i && new_x_before_i) {                    // x 原来不排在 i 前面,现在排到了前面                    // i 前面多了一个元素,排名后移 1 位                    rnk[i]++;                }            }            // 重新计算 x 自身的排名            rnk[x] = 1;            for (int j = 1; j <= n; j++) {                if (j == x) {                    continue;                }                if (a[j] < a[x] || (a[j] == a[x] && j < x)) {                    rnk[x]++;                }            }        } else {            // 查询操作:输出 a[x] 排序后所处的位置            int x;            std::cin >> x;            std::cout << rnk[x] << "\n";        }    }    return 0;}

【推荐】【GESP】C++ 认证学习资源汇总(2026年3月更新)

【推荐】GESP/CSP/NOI资料站https://wiki.coderli.com/

【推荐】GESP/CSP学习交流群

【CSP】CSP-J 2021真题 | 插入排序 luogu-P7910 (适合GESP四-六级及以上考生练习) 第16张

luogu-”系列题目可在洛谷题库进行在线评测。

bcqm-”系列题目可在编程启蒙题库进行在线评测。

抱歉,评论功能暂时关闭!