【luogu AT1218】たのしい家庭菜園 / 交换(贪心)(树状数组)

たのしい家庭菜園 / 交换

题目链接:luogu AT1218

题目大意

给你一个数组,你每次可以交换两个相邻的位置。
问你要交换多少次才能使得这个数组变成先上升后下降的形式。

思路

不难看到一个贪心的做法,肯定是从小到大每次把它移到左边或者右边。

那你就可以分别求出移到左边和右边的费用,然后每个都选最小值的即可。

然后移到一边的费用大概就是比它大的个数(因为这些要被移动出来),那这个我们用两次树状数组求即可。

代码

#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long

using namespace std;

int n, a[300001], f[300001], g[300001], tr[300001];
int b[300001];
ll ans;

void Add(int x, int y) {
    
    //树状数组
	for (; x <= n; x += x & (-x)) tr[x] += y;
}

int Ask(int x) {
    
    
	int re = 0;
	for (; x; x -= x & (-x)) re += tr[x];
	return re;
}

int main() {
    
    
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
    
    
		scanf("%d", &a[i]);
	}
	
	memcpy(b, a, sizeof(b));
	sort(b + 1, b + n + 1);//离散化
	int nn = unique(b + 1, b + n + 1) - b - 1;
	for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + nn + 1, a[i]) - b;
	
	for (int i = 1; i <= n; i++) {
    
    
		f[i] = i - 1 - Ask(a[i]);
		Add(a[i], 1);
	}
	for (int i = 1; i <= n; i++) Add(a[i], -1);
	
	for (int i = n; i >= 1; i--) {
    
    
		g[i] = n - i - Ask(a[i]);
		Add(a[i], 1);
	}
	
	for (int i = 1; i <= n; i++)
		ans += min(f[i], g[i]);
	printf("%lld\n", ans);//Atcoder特色换行?(似乎不换行会WA
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/121352139