【ybt高效进阶4-2-2】【luogu P1908】逆序对

逆序对

题目链接: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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/114761364
今日推荐