逆序对
题目链接:ybt高效进阶4-2-2 / luogu P1908
题目大意
求一个序列的逆序对个数。
思路
我们考虑看对于每个位置,有多少个在它前面的数比它大。
(没存在这么一个数,它就可以和你这个数组成一个逆序对)
那我们考虑用树状数组弄。
树状数组能用小于等于一个数的个数,那我们用全部在它前面的数减去其中小于等于它的个数,就是大于它的个数。
那就有想法了,从左到右枚举数,然后放进去树状数组并且求上面的东西。然后把每次求出来的加起来,就是总的逆序对个数了。
因为树状数组这么搞存的是数值,我们就要把数组离散化一下,重新按大小编个号。就可以了,反正你逆序对只是大小关系,只改编号不改大小关系是没有问题的。
代码
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
struct node {
int x, num, lx;
}a[500001];
ll tree[500001], ans;
int n;
bool cmp1(node x, node y) {
return x.x < y.x;
}
bool cmp2(node x, node y) {
return x.num < y.num;
}
void lsh() {
int tmp = 0;
sort(a + 1, a + n + 1, cmp1);
a[1].lx = ++tmp;
for (int i = 2; i <= n; i++)
if (a[i].x != a[i - 1].x) a[i].lx = ++tmp;
else a[i].lx = tmp;
sort(a + 1, a + n + 1, cmp2);
}
void add(int x, ll y) {
for (int i = x; i <= n; i += i & (-i))
tree[i] += y;
}
ll ask(int x) {
ll re = 0;
for (int i = x; i; i -= i & (-i))
re += tree[i];
return re;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i].x);
a[i].num = i;
}
lsh();//离散化重新编号
for (int i = 1; i <= n; i++) {
add(a[i].lx, 1ll);
ans += 1ll * i - ask(a[i].lx);//逆序对=所有的对-顺序对
}
printf("%lld", ans);
return 0;
}