#P2446. 逆序对.线段树.填空题
逆序对.线段树.填空题
题目描述
猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。
最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 且 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。
输入格式
第一行,一个数 ,表示序列中有 个数。
第二行 个数,表示给定的序列。序列中每个数字不超过 。
输出格式
输出序列中逆序对的数目。
样例
6
5 4 2 6 3 1
11
数据规模
对于 的数据,
对于 的数据,。
对于所有数据,
完成程序
下面的程序基于树状数组的算法。
#include<bits/stdc++.h>
using namespace std;
#define mid (tr[idx].left+tr[idx].right)/2
#define LC idx*2
#define RC idx*2+1
int n;
struct Node {
int val, pos;
bool operator < (const Node& a) const {
return val < a.val||(val==a.val&&pos<a.pos);
}
} a[100001];
struct TreeNode{
int left,right,sum;
}tr[400001]; // 至少要 4 倍的 n
void create_tree(int idx,int l,int r){
// 建树的时候,没有数据,所以 sum 为 0 ,不需要初始化
tr[idx].left = l;
tr[idx].right = r;
if(__填空(1)__){
create_tree(LC,l,mid);
create_tree(__填空(2)__);
}
}
void update(int idx,int x){ // 在 下标为 x 到地方加 1
if(tr[idx].left > x || tr[idx].right < x) __填空(3)__;
if(tr[idx].left==tr[idx].right && tr[idx].left==x) __填空(4)__;
else{
update(LC,x);
update(RC,x);
__填空(5)__;
}
}
int query(int idx,int l, int r){ // 查询下标 [l,r] 的和
if(tr[idx].left>r || tr[idx].right < l ) return 0;
if(tr[idx].left>=l && tr[idx].right<=r) return tr[idx].sum;
return __填空(6)__;
}
int main(){
scanf("%d", &n);
create_tree(1,1,n); // 初始化线段树
for (int i =1; i <=n; i++){
scanf("%d", &a[i].val);
a[i].pos = i;
}
sort(a+1, a+n+1);
// 排序之后,完成离散化,a[i].pos 记录了第 i 小的数原来的位置。
long long ans = 0;
// 从小到大把数字加入到树状数组(加入的时候,是按照原来的位置去加 1)
for (int i = 1; i <= n; i++){
// 加到第 i 小的数字的时候,第 1 小到第 i-1 小的数已经在树状数组中,有一部分在 a[i].pos 前面,有一部分在 a[i].pos 后面
// 第 i 小的数可以和 a[i].pos 位置后面的数(比 a[i].val 小的哪些)组成逆序对
ans += query(__填空(7)__);
update(__填空(8)__);
}
printf("%lld", ans);
}
填空(1): {{ input(1) }}
填空(2): {{ input(2) }}
填空(3): {{ input(3) }}
填空(4): {{ input(4) }}
填空(5): {{ input(5) }}
填空(6): {{ input(6) }}
填空(7): {{ input(7) }}
填空(8): {{ input(8) }}