[bzoj4240]有趣的家庭菜园_树状数组

有趣的家庭菜园

题目链接https://lydsy.com/JudgeOnline/problem.php?id=4240

数据范围:略。


题解

第一步比较简单,只需要排序之后,每个数不是在左边就是在右边。

我们只需要计算出,当前这个数如果在左边,能跟没放进数列的数构成的逆序对数和在右边哪个更小。

这个过程可以用树状数组维护。

代码

#include <bits/stdc++.h>

#define N 1000010 

using namespace std;

typedef long long ll;

char *p1, *p2, buf[100000];

#define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ )

int rd() {
	int x = 0, f = 1;
	char c = nc();
	while (c < 48) {
		if (c == '-')
			f = -1;
		c = nc();
	}
	while (c > 47) {
		x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
	}
	return x * f;
}

int tr[N];

inline int lowbit(int x) {
	return x & (-x);
}

void upd(int x, int val) {
	for (int i = x; i < N; i += lowbit(i)) {
		tr[i] += val;
	}
}

int qry(int x) {
	int ans = 0;
	for (int i = x; i; i -= lowbit(i)) {
		ans += tr[i];
	}
	return ans;
}

struct Node {
	int val, id;
}a[N];

inline bool cmp(const Node &a, const Node &b) {
	return a.val > b.val;
}

int main() {
	int n = rd();
	for (int i = 1; i <= n; i ++ ) {
		a[i].val = rd(), a[i].id = i;
	}
	ll ans = 0;
	sort(a + 1, a + n + 1, cmp);
	for (int i = 1; i <= n; ){
		int j;
		for (j = i; j <= n; j ++ ) {
			int mdl = qry(a[j].id);
			ans += min(mdl, i - 1 - mdl);
			if (a[j + 1].val != a[j].val) {
				break;
			}
		}
		for (; i <= j; i ++ ) {
			upd(a[i].id, 1);
		}
	}

	cout << ans << endl ;
	return 0;
}
posted @ 2019-10-31 20:07  JZYshuraK_彧  阅读(147)  评论(0编辑  收藏  举报