树状数组求解逆序对数量详解
创作时间:
作者:
@小白创作中心
树状数组求解逆序对数量详解
引用
CSDN
1.
https://blog.csdn.net/2301_80328768/article/details/138295453
现在,给你一个数组a[]={5,4,2,6,3,1}
那么树状数组是怎么求出逆序对的数量的呢?
首先,给出一个朴素求解逆序对数量的思路:
对于一个数组a[],
使用for循环对其进行遍历,
当遍历到i时,再使用一个for循环从(1->i-1)
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (a[j] > a[i]) {
ans++;
}
}
}
那么可以使用树状数组对内层循环进行优化
上述代码可以简单的表述为:
for (int i = 1; i <= n; i++) {
//循环到i
//ans+=(从1到i-1)大于i的数量
//注意上方的(从1到i-1)不是下标
// i
//5 3 4 2 1
//比如a[i]=4
//ans+=(1,3)出现的数量
}
for (int i = n; i >= 1; i--) {
//循环到i
//ans+=(1到i-1)小于i的数量
//注意上方的(从1到i-1)不是下标
// i
//5 3 4 2 1
//比如a[i]=4
//ans+=(1,3)出现的数量
}
所以 用树状数组s[x]表示[x-lowbit(x)+1,x]的和
sum(x)表示[1,x]数量和
如果在某个时刻,进入一个新数字6,
需要先调用树状数组的add函数维护:add(6,1)
我们只需要调用树状数组的函数sum(6-1)求出[1,5]的数量即可
接下来展示更详细:
从后向前:
从后来每进来一个数字调用add函数维护sx
比如add(1,1),调用函数时s[1]+1,s[2]+1,s[4]+1,s[8]+1.为什么s[3],s[5]..不加1?
因为s[x]表示[x-lowbit(x)+1,x]的范围 你这个范围内包含1 才会加1
答案加等于sum(a[i]-1),比如来到上图的4,查询(1,3)的数量(因为时从后向前 所以这(1,3)一定在4的后面)构成逆序对
//后
for (int i = n; i >= 1; i--) {
add(a[i], 1);
ans += sum(a[i] - 1);
}
从前往后:
如图 来到3 我们寻找前面比它大的数字 可以用(下标-小于等于a[i]的数字==比他大的数字)
//前
for (int i = 1; i <= n; i++) {
add(a[i], 1);
ans += i - sum(a[i]);
}
最后 离散化 可能数据会很大
例如 999 998 997 996 995
离散 5 4 3 2 1
"大于 等于"是相对的关系 所以离散化没关系 可以减少空间
网上许多离散化特别魔怔 大家用这个就行
for (int i = 1; i <= n; i++) {
cin >> a[i];
b[i] = a[i];
}
sort(b + 1, b + n + 1);
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
}
例题
完整代码
//先介绍一下树状数组怎么求逆序对
//如果 从前向后 就是add(a[i],1) ans+=i-sum(a[i])
//如果 从后向前 就是add(a[i],1) ans+=sum(a[i]-1)
#include <iostream>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int N = 500005;
int n, a[N], b[N], s[N];
void add(int x, int k) {
while (x <= n) {
s[x] += k, x += x & -x;
}
}
int sum(int x) {
int t = 0;
while (x) {
t += s[x], x -= x & -x;
}
return t;
}
signed main() {
cin >> n; int ans = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
b[i] = a[i];
}
sort(b + 1, b + n + 1);
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
}
// //前
//for (int i = 1; i <= n; i++) {
// add(a[i], 1);
// ans += i - sum(a[i]);
//}
//后
for (int i = n; i >= 1; i--) {
add(a[i], 1);
ans += sum(a[i] - 1);
}
cout << ans << endl;
}
热门推荐
临时建筑使用年限怎么确定?
对方转移财产怎么举证?这些证据类型和收集方法请收好
债务人转移财产怎么证明?与债务重组有何区别?
瓦楞纸箱的优点与应用解析,助您选择最佳包装方案
美国学校文化的多样性与特色
春季,饮食宜淡不宜咸!建议多吃这8样,只在春天才能吃到
tRPC 代码示例详细解析:逐行理解及编程思想探究
面对网络谣言,如何增强“免疫力”
待摊费用的账务处理方法有哪些具体步骤?
全面指南:如何规划天涯海角旅游行程及必备攻略
外贸营销法律亮点:合规与风险防范的关键策略
日本樱花的意义是什么?日本樱花的寓意是什么?
作物为什么使用氮肥,氮元素在作物生长中起什么作用呢?
这种类型糖尿病偏爱青少年,家长要警惕孩子“三多一少”
周杰伦歌曲排名前十 亚洲天王周杰伦歌曲排行榜top10
陈都灵,终于出圈了
陈都灵《大梦归离》《永夜星河》都好美,2年播11部剧强势霸屏
斗鱼品种全解析:色彩斑斓的水中精灵!
枸杞泡茶的四种搭配:一补气、二明目、三润燥、四安神
10句顶级治愈诗词,词墨交织的瞬间,邂逅生命中的心灵共振
杭州三月限定,西湖边古风大片机位及烟花赏花攻略
宠物何时接种疫苗?咨询兽医知最佳时间。
人和动物如何通过基因区别?
小孩腿烂出水怎么办
杭州站和杭州东站有什么区别
【液晶屏清洁剂选购】液晶清洁剂如何选购 液晶清洁剂产品选择误区
大学生如何面对未来:平衡现实和理想
银行的金融服务个性化服务设计与客户关系维护研究
程斌为什么出卖杨靖宇?程斌结局如何?程斌又是怎么被发现的?
4个叛徒出卖杨靖宇将军后下场如何?杨将军死后留下1件遗物