浅谈逆序对在算法竞赛中的具体运用
目录逆序对简介逆序对能做什么一些逆序对杂题总结逆序对简介逆序对定义给定一个序列\(a\)存在有序对\((i,j)\)满足\(ij\)且\(a_i a_j\)则称\((i,j)\)为一个逆序对。如何求序列逆序对对数根据定义对于一个下标\(i\)它能产生的所有逆序对\((i,j)\)即为区间\([i1,n]\)中小于\(a_i\)的数的个数。最朴素的\(O(n^2)\)做法如下/* by 01022.hk - online tools website : 01022.hk/zh/httpheader.html */ for (int i 1 ; i n ; i) { for (int j i1 ; j n ; j) { if (a[j] a[i]) cnt; } }这个过程可以通过树状数组或线段树优化从后往前依次插入\(a_i\)并通过树状数组查询小于\(a_i\)的数的个数。当然从前往后插入\(a_i\)并查询大于\(a_i\)的数的个数同样可以。以上做法复杂度为\(O(nlogn)\)。还有一种通过归并排序求逆序对的做法这里不做赘述。模版题 P1908 逆序对本题需要离散化后再建树状数组。以下采用的是从后往前插入的方法。/* by 01022.hk - online tools website : 01022.hk/zh/httpheader.html */ #include iostream #include algorithm #include vector using namespace std; using ll long long; const int N 5e5 10; vectorint vec; int a[N],tr[N]; int siz; int lowbit(int x) { return x -x; } void insert(int x) { while (x siz) { tr[x]; x lowbit(x); } } ll query(int x) { ll sum 0; while (x) { sum tr[x]; x - lowbit(x); } return sum; } int id(int x) { return lower_bound(vec.begin(),vec.end(),x) - vec.begin() 1; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin n; for (int i 1 ; i n ; i) { cin a[i]; vec.push_back(a[i]); } //离散化 sort(vec.begin(),vec.end()); auto e unique(vec.begin(),vec.end()); vec.erase(e,vec.end()); siz vec.size(); ll ans 0; for (int i n ; i 1 ; i--) { if (!a[i]) continue; ans query(id(a[i])-1); insert(id(a[i])); } cout ans endl; return 0; }逆序对能做什么1、逆序对数量 冒泡排序元素交换次数这个结论也可以说是通过相邻交换得到升序排序的最小操作数。如何理解这个结论直观感受当冒泡排序进行到\([1,i-1]\)都已经排列有序时此时要移动下标\(i\)位置的元素到对应位置。这个元素要交换的次数就等于它左边比它大的元素个数。实际上对下标\(i\)求前面比它大的元素个数这就是在求逆序对。举个例子\(\{[1,3,4],2,5\}\)中当对\(2\)进行冒泡排序时数组会变为\(\{[1,2,3,4],5\}\)也就是和\(4、3\)依次交换。从逆序对的角度讲每一次相邻交换都会使逆序对的数量减1。因此逆序对数量就等于元素交换次数。裸题 P1116 车厢重组这题数据比较水甚至可以不用树状数组。以下是树状数组从前往后插入的写法。#include iostream #include algorithm #include vector using namespace std; using ll long long; const int N 1010; int a[N],tr[N]; int n; int lowbit(int x) { return x -x; } void insert(int x) { while (x n) { tr[x]; x lowbit(x); } } ll query(int x) { ll sum 0; while (x) { sum tr[x]; x - lowbit(x); } return sum; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin n; ll ans 0; for (int i 1 ; i n ; i) { cin a[i]; insert(a[i]); ans query(n) - query(a[i]); } cout ans endl; return 0; }再看一道非常经典的题目P1966 [NOIP 2013 提高组] 火柴排队题意有\(a、b\)两个数组都支持数组内相邻元素交换求 $ \sum (a_i-b_i)^2$ 最小化时最少交换次数答案对\(10^8-3\)取模。本题难点在于如何最小化 $ \sum (a_i-b_i)^2$。这里直接说结论将\(a\)和\(b\)数组分别排序后对应位置的\(a_i\)和\(b_i\)在同一行时该式最小。该策略可以用微扰法证明这不是本篇的重点详细证明可参考洛谷题解区于是问题转化为使\(a\)和\(b\)中排名相同的元素都处于同一行需要的最少交换次数。首先当两个数组都可以交换时一定可以只对一个数组进行操作。因为对\(a\)中的两个元素进行交换和交换\(b\)中对应位置的元素是等价的。类似求最长公共子序列的方法我们固定数组\(b\)不动将\(a_i\)应该移动到的位置映射出来将\(a\)按映射值排序就可以把问题转化成求升序排列的最小交换次数。例如\(a [1,3,2,4],b [4,3,2,1]\)a原值 1 3 2 4 b原值 4 3 2 1 a映射 4 2 3 1线段树实现#include iostream #include vector #include algorithm #include map using namespace std; const int MOD 1e8 - 3; const int N 1e5 10; mapint,int mp1,mp2,mp3; int a[N],b[N]; int a1[N],b1[N]; int tr[4*N]; void push_up(int pos) { tr[pos] tr[pos*2] tr[pos*21]; } void add(int pos, int l, int r, int aim) { if (l aim r aim) { tr[pos] 1; return; } int mid (lr) 1; if (aim mid) add(pos*2,l,mid,aim); else add(pos*21,mid1,r,aim); push_up(pos); } int query(int pos, int l, int r, int ql, int qr) { if (qr ql) return 0; if (ql l qr r) return tr[pos]; int mid (lr) 1; int sum 0; if (ql mid) sum query(pos*2,l,mid,ql,qr); if (qr mid) sum query(pos*21,mid1,r,ql,qr); return sum; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin n; for (int i 1 ; i n ; i) { cin a[i]; a1[i] a[i]; } for (int i 1 ; i n ; i) { cin b[i]; b1[i] b[i]; } sort(a11,a1n1); sort(b11,b1n1); for (int i 1 ; i n ; i) { mp1[a1[i]] i; mp2[b1[i]] i; } for (int i 1 ; i n ; i) { a[i] mp1[a[i]]; b[i] mp2[b[i]]; mp3[b[i]] i; } for (int i 1 ; i n ; i) a[i] mp3[a[i]]; int ans 0; for (int i n ; i 1 ; i--) { add(1,1,n,a[i]); ans query(1,1,n,1,a[i]-1); ans % MOD; } cout ans endl; return 0; }2、判断数组变化的可达性这句话比较抽象。具体点就是一个数组通过元素交换、区间翻转等操作后能否得到另一个数组。由于这些操作一般都伴随着逆序对的变化所以可通过逆序对的数量变化、奇偶性变化等来判断是否可达。CF2101B Quartet Swapping题意给定一个长度为\(n\)的排列。支持一种操作选择一个下标\(1 \leq i \leq n-3\)同时交换\(a_i , a_{i2}\)以及\(a_{i1},a_{i3}\)。求任意次操作后可达的字典序最小的排列。注意到奇数位置只会和奇数位置交换偶数位置只会和偶数位置交换。这启示我们分奇偶来看。手玩样例发现除了\(a_{n-2}\)和\(a_n\)其他位置可以构造出最优解。即可能出现\(a_{n-2} a_n\)。这种涉及到元素交换的题目都可以往逆序对方向想想。本题可以将奇数位置和偶数位置单独拿出来计算逆序对。考虑一次操作会对逆序对产生什么影响。一对元素的交换必定导致逆序对的数量1或-1也就是奇偶性翻转。由于奇位置和偶位置各有一对元素交换因此奇数位置的逆序对奇偶性和偶数位置的逆序对奇偶性同时变化。字典序最小的排列中奇数位置和偶数位置的逆序对均为0因此只有当奇偶位置逆序对奇偶性相同时才可能构造出最优解否则交换\(a_n\)和\(a_{n-2}\)。#include iostream #include algorithm #include vector using namespace std; using ll long long; int n; int lowbit(int x) { return x -x; } ll count(vectorint a) { vectorint s(n10); auto update [](int x) { while (x n) { s[x]; x lowbit(x); } }; auto query [](int x) { int t 0; while (x) { t s[x]; x - lowbit(x); } return t; }; ll sum 0; for (int i a.size()-1 ; i 0 ; i--) { update(a[i]); sum query(a[i]-1); } return sum; } void solve() { cin n; vectorint a(n10); vectorvectorint vec(2); ll cnt1 0, cnt2 0; for (int i 1 ; i n ; i) { cin a[i]; vec[i%2].push_back(a[i]); } cnt1 count(vec[0]); cnt2 count(vec[1]); sort(vec[0].begin(),vec[0].end()); sort(vec[1].begin(),vec[1].end()); int p[2] {0}; for (int i 1 ; i n ; i) a[i] vec[i%2][p[i%2]]; if (cnt1 % 2 ! cnt2 % 2) swap(a[n],a[n-2]); for (int i 1 ; i n ; i) cout a[i] ; cout endl; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int T; cin T; while (T--) solve(); return 0; }P10454 奇数码问题题意\(n \times n\)的矩阵中有\(1\)个空格和\(1 \sim n^2-1\)这几个数。空格可以进行上、下、左、右移动。问初始局面和最终局面是否是可达的。一道非常邪门的题目。将矩阵按行拆开排列成一维数组(忽略这个空格位置)。手动模拟几轮后注意到空格的左、右移动不会改变这个数组逆序对奇偶性不变。而上、下移动相当于把一个元素在这个数组中移动了\(n-1\)位。由于\(n\)为奇数因此逆序对奇偶性也不会发生改变。因此仅当将初始局面和最终局面拍扁成一维数组后逆序对奇偶性不变时它们是互相可达的。这题提供了一种思路矩阵的某些操作也可以通过一些方式转化为判断逆序对的性质。#include iostream #include unordered_map #include vector using namespace std; void push_up(int pos, vectorint tr) { tr[pos] tr[pos*2] tr[pos*21]; } void update(int pos, int l, int r, int k, vectorint tr) { if (l k r k) { tr[pos]; return; } int mid (lr) 1; if (k mid) update(pos*2,l,mid,k,tr); else update(pos*21,mid1,r,k,tr); push_up(pos,tr); } long long query(int pos, int l, int r, int ql, int qr, vectorint tr) { if (ql l qr r) return tr[pos]; int mid (lr) 1; long long sum 0; if (ql mid) sum query(pos*2,l,mid,ql,qr,tr); if (qr mid) sum query(pos*21,mid1,r,ql,qr,tr); return sum; } int n; void solve() { vectorvectorint a(n10,vectorint(n10)); vectorvectorint b(n10,vectorint(n10)); for (int i 1 ; i n ; i) { for (int j 1 ; j n ; j) { cin a[i][j]; // cout a[i][j] ; } // cout endl; } for (int i 1 ; i n ; i) { for (int j 1 ; j n ; j) { cin b[i][j]; // cout b[i][j] ; } // cout endl; } vectorint c(n*n10),d(n*n10); int cnt1 0, cnt2 0; for (int i 1 ; i n ; i) { for (int j 1 ; j n ; j) { c[cnt1] a[i][j]; d[cnt2] b[i][j]; } } int N n * n; vectorint tr1(4*N10),tr2(4*N10); long long sum1 0, sum2 0; for (int i N ; i 1 ; i--) { if (c[i] 0) continue; update(1,1,N,c[i],tr1); if (c[i] ! 1) sum1 query(1,1,N,1,c[i]-1,tr1); } for (int i N ; i 1 ; i--) { if (d[i] 0) continue; update(1,1,N,d[i],tr2); if (d[i] ! 1) sum2 query(1,1,N,1,d[i]-1,tr2); } if ((sum1 % 2) (sum2 % 2)) cout TAK endl; else cout NIE endl; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); while (cin n) solve(); return 0; }注意通过逆序对性质判断可达性一般要证明充要性。以 Quartet Swapping 这题为例只有当奇偶位置逆序对的奇偶性相同时才可能构造出最优解这个结论是很草率的因为我们实际只能看出它的必要性即能构造出最优解时奇偶位置独立算的逆序对的奇偶性一致。这题的充分性证明比较困难至少我不会但有一种比较无脑的方法就是归纳法。在小样例的情况下手动模拟猜测充分性是否成立。一些逆序对杂题CF911D Inversion Counting 区间翻转对逆序对的影响2024ICPC沈阳D题 通过奇偶做博弈分析2025牛客多校第七场A题 类似奇数码问题(官方视频题解)总结涉及到元素交换、元素移动、区间循环左/右移、区间翻转的题目都可以往逆序对方向考虑。逆序对比起反映序列性质我觉得更像反映了某些操作的性质。

相关新闻

计算机毕设java助学金管理系统 高校学生资助信息管理平台 校园奖助贷一体化服务系统

计算机毕设java助学金管理系统 高校学生资助信息管理平台 校园奖助贷一体化服务系统

计算机毕设java助学金管理系统qkv0p9(配套有源码 程序 mysql数据库 论文) 本套源码可以在文本联xi,先看具体系统功能演示视频领取,可分享源码参考。当“不让一个学生因家庭经济困难而失学”成为国家共识,高校资助业务却仍在用纸质…

2026/7/5 2:22:32 阅读更多 →
格式总出错?AI论文工具 千笔 VS 文途AI,本科生写作更轻松!

格式总出错?AI论文工具 千笔 VS 文途AI,本科生写作更轻松!

随着人工智能技术的迅猛发展,AI辅助写作工具逐渐成为高校学生完成毕业论文的重要助手。越来越多的学生开始借助这些工具提升写作效率、优化内容结构,以应对日益繁重的学术任务。然而,面对市场上功能各异、质量参差不齐的AI工具,许…

2026/7/3 14:18:21 阅读更多 →
java--线程安全问题

java--线程安全问题

概念:Java线程安全问题,本质上是在多线程环境下,由于线程调度的不确定性,导致程序的执行结果与预期不符。 线程安全问题的产生通常可以归结为以下三个核心原因: 原子性问题 (Atomicity) 一个看似简单的操作&#xff0…

2026/7/3 14:18:25 阅读更多 →

最新新闻

AI服务合规网关实战:GDPR日志脱敏、国密SM4加密与审计追踪

AI服务合规网关实战:GDPR日志脱敏、国密SM4加密与审计追踪

1. 项目概述:一场迫在眉睫的合规风暴最近在排查一个线上AI服务的问题时,我遇到了一个典型的报错:cc switch deepseek unexpected status 502 bad gateway: unknown error, url: ht...。这个错误本身指向的是服务网关的切换或配置问题&#xf…

2026/7/5 10:35:10 阅读更多 →
光伏逆变器LVRT技术:Boost+NPC拓扑设计与控制策略

光伏逆变器LVRT技术:Boost+NPC拓扑设计与控制策略

1. 光伏逆变器低电压穿越技术概述 光伏发电系统在电网电压骤降时能否保持并网运行,直接关系到整个电力系统的稳定性。低电压穿越(LVRT)技术就是让逆变器在电网电压跌落时,不仅不脱网还能向电网提供无功功率支撑的关键能力。传统方案中,当检测…

2026/7/5 10:33:10 阅读更多 →
Allen Bradley 80190-378-51/12控制器板功能与应用解析

Allen Bradley 80190-378-51/12控制器板功能与应用解析

1. Allen Bradley 80190-378-51/12控制器板概述Allen Bradley 80190-378-51/12控制器板是罗克韦尔自动化旗下Allen-Bradley品牌推出的一款工业级控制电路板。作为自动化控制系统中的核心组件,它主要负责信号采集、逻辑运算和设备控制等功能。这款控制器板采用成熟的…

2026/7/5 10:31:10 阅读更多 →
解锁网易云音乐加密格式:ncmdump工具的全面应用指南

解锁网易云音乐加密格式:ncmdump工具的全面应用指南

解锁网易云音乐加密格式:ncmdump工具的全面应用指南 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 你是否曾经遇到过这样的困扰:在网易云音乐下载的歌曲只能在特定应用内播放,无法在其他设备或播…

2026/7/5 10:31:10 阅读更多 →
I型NPC三电平逆变器SVPWM仿真设计与控制策略

I型NPC三电平逆变器SVPWM仿真设计与控制策略

1. I型NPC三电平逆变器SVPWM仿真设计概述在电力电子领域,三电平逆变器因其输出电压谐波含量低、开关损耗小等优势,已成为中高压大功率应用的首选拓扑结构。I型NPC(Neutral Point Clamped)三电平逆变器通过钳位二极管将直流母线中点…

2026/7/5 10:29:09 阅读更多 →
电源环设计:PCB供电优化的核心技术解析

电源环设计:PCB供电优化的核心技术解析

1. 电源环是什么?电源环(Power Ring)是电子设备中一种特殊的环形电源分配结构。我第一次接触这个概念是在设计一块高密度PCB板时,当时为了解决多芯片供电的电压跌落问题,老工程师建议我试试电源环布局。简单来说&#…

2026/7/5 10:27:09 阅读更多 →

日新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

月新闻