2021年第十二届蓝桥杯 - 省赛 - C/C++大学B组 - I.双向排序


Ideas

题目中给出了两种操作:

  1. 当 pi = 0 时,表示将 a1, a2, · · · , aqi 降序排列;
  2. 当 pi = 1 时,表示将 aqi , aqi+1, · · · , an 升序排列。

按照题目暴力排序应该可以骗一点分,但如果想AC,就需要优化算法。可以根据测试用例的范围:1 ≤ n, m ≤ 100000,估计一下时间复杂度要控制在O(nlog n)。

首先对于连续的p=0,即:pi=0 qi=a;pi+1=0 qi+1=b。如果b>a,那么(pi, qi)的操作将无效,因为(pi+1, qi+1)已经将(pi, qi)的范围包含了。同理,如果pi+2=0; qi+2=c,而b>c,那么pi+2和qi+2的操作也将无效。

然后对于连续的p=1,即:pi=1 qi=a;pi+1=1 qi+1=b。同理,如果a<b,那么(pi+1, qi+1)的操作将无效,因为(pi, qi)已经将(pi+1, qi+1)的范围包含了。

因此我们可以总结出,对于连续的p=0和p=1,只需要分别保留q最大和q最小的那次操作即可。

有了上面的简化之后,整个操作序列其实就被压缩成了p=0和p=1的交替操作,由于一开始的序列是升序排列的,所以第一个有效操作肯定是pi = 0,让我们将某一个前缀降序排列。

我们举个例子来分析一下,令n=9,即[1, 2, 3, 4, 5, 6, 7, 8, 9]:

  1. p=0,q=3,即1~3位置降序排,变成了[3, 2, 1, 4, 5, 6, 7, 8, 9]
  2. p=1,q=7,即7~9位置升序排,不发生变化,还是[3, 2, 1, 4, 5, 6, 7, 8, 9]
  3. p=0,q=6,即1~6位置降序排,变成了[6, 5, 4, 3, 2, 1, 7, 8, 9]
  4. p=1,q=4,即4~9位置升序排,变成了[6, 5, 4, 1, 2, 3, 7, 8, 9]
  5. p=0,q=5,即1~5位置降序排,变成了[6, 5, 4, 2, 1, 3, 7, 8, 9]

通过这个例子我们可以发现一些规律,有些数字的位置被固定下来了,基本不会变。

为了会这样呢?我们分析一下。

对于第1次有效操作,假设是将[1, x]降序排列,由于最初我们的数据都是升序的,所以∀b∈[x+1, n] > ∀a∈[0, x]。

那么之后我们对[y, n]升序排列(y<=x) 的话其实[x, n]这部分是不变的。

这是因为[y, x] ∈ [0, x] < [x+1, n],所以[x+1, n]这部分的任意值是始终大于[y, x]中的任意值。

因此我们对[y, n]升序排序的话,其实[x, n]这部分不会挪动位置,换句话说,[x, n]已经被固定下来了。

同理,[1, y]其实也被固定下来了,所以说我们不停的操作其实就是不停的从数组的两边向中间固定元素。

我们可以用两个变量 left 和 right,分别表示数组的两边被固定下来的位置,left 不断增加,right 不断减小,最终数组被固定。

最后扣一下边界,如果最后一次操作是前缀降序,那相当于确定一个[x, n],我们手动把一个[i, x]确定下来就可以了,反之亦然。

Code

C++

#include <iostream>using namespace std;const int N = 100010;
pair<int, int> stk[N];
int ans[N];int main() {int n, m, top = 0;cin >> n >> m;while (m--) {int p, q;cin >> p >> q;if (p == 0) {while (top && stk[top].first == 0) {q = max(q, stk[top--].second);}while (top >= 2 && stk[top - 1].second <= q) {// 如果当前操作比上一次相同操作的范围要大,那此次操作的前两次操作都将被无效化top -= 2;}stk[++top] = {0, q};} else if (top) {while (top && stk[top].first == 1) {q = min(q, stk[top--].second);}while (top >= 2 && stk[top - 1].second >= q) {// 如果当前操作比上一次相同操作的范围要大,那此次操作的前两次操作都将被无效化top -= 2;}stk[++top] = {1, q};}}int left = 1, right = n, k = n;for (int i = 1; i < top + 1; i++) {if (stk[i].first == 0) {while (right > stk[i].second && left < right + 1) {ans[right--] = k--;}} else {while (left < stk[i].second && left < right + 1) {ans[left++] = k--;}}if (left > right) {break;}}if (top % 2) {while (left < right + 1) {ans[left++] = k--;}} else {while (left < right + 1) {ans[right--] = k--;}}for (int i = 1; i < n + 1; i++) {cout << ans[i] << " ";}return 0;
}

Python

if __name__ == '__main__':n, m = map(int, input().split())nums = [i + 1 for i in range(n)]seq = []  # 用于存储操作序列for _ in range(m):p, q = map(int, input().split())if p == 0:while seq and seq[-1][0] == 0:  # 如果是连续的 p = 0,只取最大的 qq = max(q, seq[-1][1])seq.pop()while len(seq) > 1 and seq[-2][1] <= q:  # 如果此次前缀降序的右边界大于上一次前缀降序的右边界,可以省略在此之前的两次操作seq.pop()seq.pop()seq.append((0, q))elif seq:  # seq 不为空保证这是有一个 p = 0 之后的 p = 1 操作while seq and seq[-1][0] == 1:  # 如果是连续的 p = 1,只取最小的 qq = min(q, seq[-1][1])seq.pop()while len(seq) > 1 and seq[-2][1] >= q:  # 如果此次后缀升序的左边界小于上一次后缀升序的右边界,可以省略在此之前的两次操作seq.pop()seq.pop()seq.append((1, q))k, left, right = n, 1, nfor i in range(len(seq)):if seq[i][0] == 0:  # 前缀降序while right > seq[i][1] and left <= right:nums[right - 1] = k       # 从后往前设置right -= 1k -= 1else:  # 后缀升序while left < seq[i][1] and left <= right:nums[left - 1] = k       # 从前往后设置left += 1k -= 1if left > right:breakif len(seq) % 2:   # 最后一次操作为前缀降序while left <= right:nums[left - 1] = kleft += 1k -= 1else:                # 最后一次操作为后缀升序while left <= right:nums[right - 1] = kright -= 1k -= 1print(' '.join(map(str, nums)))

2021年第十二届蓝桥杯 - 省赛 - C/C++大学B组 - I.双向排序相关推荐

  1. 2021年第十二届蓝桥杯 - 省赛 - C/C++大学A组 - D.路径

    2021年第十二届蓝桥杯 - 省赛 - C/C++大学A组 - D.路径 Ideas 算法:最短路径 数据结构:图 思路:根据规则构图,单源最短路径Dijkstra算法. 首先构图其实很简单,就是按照 ...

  2. 2021年第十二届蓝桥杯 - 省赛 - C/C++大学C组 - D.相乘

    Ideas 对于Python来说,都没有溢出,直接乘就完了. Code Python if __name__ == '__main__':for num in range(1, 1000000008) ...

  3. 2021第十二届蓝桥杯国赛总结-java大学c组

    比赛结果 结果挺意外的,比赛中失误较多,送分题暴毙(审题失误),没把自己气死,结果算不错,但也挺可惜. 解答过程 1. 整数范围 答案:255 没想太多直接计算器算的,后来听符号位,但似乎不用考虑. ...

  4. 第十二届蓝桥杯省赛 C/C++大学B组 试题G:砝码称重

    试题题目: 本题为编程题第二题 解题思路: 方法一:暴力求解 1.分析    首先利用数组W[N]W[N]W[N]记录NNN块砝码的质量.    模拟放取过程.先取第一块砝码放在天平上,再取第二块砝码 ...

  5. 2021年第十二届蓝桥杯省赛C/C++B组题解总结

    前几天(2021.4.18)刚刚比完了2021年第十二届蓝桥杯省赛,本人参加的是软件组C++B组的比赛,本文包括了这一届C++B组的题目以及部分题解.感悟和总结. 目录 试题A.空间 试题B.卡片 试 ...

  6. 2021年第十二届蓝桥杯省赛B组(C/C++)第二场题解

    文章目录 2021年第十二届蓝桥杯省赛B组(C/C++)第二场题解 1.求余 2.双阶乘 3.格点 4.整数分解 5.城邦 6.特殊年份 7.小平方 8.完全平方数 9.负载均衡 10.国际象棋 20 ...

  7. 2019 第十届蓝桥杯省赛C/C++大学B组 试题+题解

    第十届蓝桥杯省赛C/C++大学B组 试题+题解 第十届蓝桥杯大赛软件类省赛 C/C++ 大学 B 组 考生须知 考试开始后,选手首先下载题目,并使用考场现场公布的解压密码解压试 题. 考试时间为 4 ...

  8. 2021年第十二届蓝桥杯省赛+国三C/C++B组参赛经历分享

    目录 一些流水账 备赛总结 语言选择 一些问题 牢骚 最近蓝桥杯报名又开始了,先预祝家人们能取得好成绩~ 一些流水账 按照惯例,先简单地自我介绍一下:本人就读于西南某不知名双非院校+计算机弱校(不是凡 ...

  9. 2021年第十二届蓝桥杯省赛 Java B组 第一场 全部题解

    暴力杯?dp杯! 重铸国二荣光,省三义不容辞 感谢评论区的大佬给出的思路还有指正的一些错误地方,现都已更正,当然如果还有其他更优解法的也欢迎评论区指出 c/c++和Java的B组题目都已更新,正在备考 ...

最新文章

  1. 先进一站式IP及定制
  2. 27.怎样在Swift中声明typedef?
  3. CGLIB依赖ASM(关于java字节码框架ASM的学习)
  4. 这就是数据分析之数据可视化基础概念及工具
  5. mysql update修改数据_MYsql如何用update语句修改数据,值得一看
  6. oc的分类category
  7. android日历信息获取错误,android – 从日历中获取事件
  8. Java面试题总结(二)
  9. 【基础教程】基于matlab生成Word+PPT报告【含Matlab源码 971期】
  10. 软件工程基础作业 可行性与需求分析
  11. win7 professional 英文版 改 中文
  12. 基于法律裁判文书的法律判决大数据预测
  13. javascript 构造函数方式定义对象 (转载)
  14. macOS下不支持双面打印功能的惠普打印机如何进行手动双面打印
  15. 所有用户账户被禁用该怎么办?
  16. SourceTree安装教程
  17. clear 和evict
  18. win10图标变白纸_#波导的效率私房# WIN10自带这么多好工具
  19. VS2022 Visual Studio 2022专业版全功能离线版下载
  20. C++ Primer 学习札记(二)

热门文章

  1. 第三方控件DevExpress的TreeList绑定XML文件
  2. LightOJ - 1236 (唯一分解定理)
  3. java基础之访问控制符
  4. hihocoder 1015 : KMP算法(kmp)
  5. leveldb 学习。
  6. Django框架Day3------之Models
  7. 关于oracle中to_char和to_date的用法
  8. [2014NoDEA]An Osgood type regularity criterion for the liquid crystal flows
  9. 菜单工具栏wxPython菜单与工具栏基础示例
  10. ise的时钟ip核_ISE的IP核clocking wizard使用和例化