たのしい家庭菜園 / 交换
题目链接: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;
}