AcWing 1215. 小朋友排队(树状数组)
n 个小朋友站成一排。
现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。
每个小朋友都有一个不高兴的程度。
开始的时候,所有小朋友的不高兴程度都是 0。
如果某个小朋友第一次被要求交换,则他的不高兴程度增加 1,如果第二次要求他交换,则他的不高兴程度增加 2(即不高兴程度为 3),依次类推。当要求某个小朋友第 k 次交换时,他的不高兴程度增加 k。
请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。
如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。
输入格式
输入的第一行包含一个整数 n,表示小朋友的个数。
第二行包含 n 个整数 H1,H2,…,Hn,分别表示每个小朋友的身高。
输出格式
输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。
数据范围
1≤n≤100000,
0≤Hi≤1000000
输入样例:
3
3 2 1
输出样例:
9
样例解释
首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。
1 5 2 4 3 6
1 2 5 4 3 6
1 2 4 5 3 6
1 2 4 3 5 6
1 2 3 4 5 6
1 2 5 3 4 6
首先题目要求相邻交换=有序,可以看出来是进行类似冒泡排序的操作。要求不高兴度最低就是要找到最优解。然后我们发现对于每个小朋友,不高兴度是固定下来的,即交换次数是固定的。联想到逆序对,每个小朋友的交换次数就是在他前面比他高的小朋友的个数k1加上在他后面比他矮的小朋友的个数k2(由于身高一样肯定选择不换,因此要严格高or严格矮)。逆序对求解可以两次树状数组统计。最后用等差数列公式累加一遍答案即可。
#include <iostream>
#include <cstring>
#define N 1000005
using namespace std;
int n, h[N];
int c[N] = { 0 };
int lft[N], rht[N];
void add(int x, int y)
{
for (; x <= N; x += (x & -x)) c[x] += y;
}
int ask(int x)
{
int ans = 0;
for (; x; x -= (x & -x)) ans += c[x];
return ans;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> h[i];
for (int i = 1; i <= n; i++)
{
lft[i] = ask(N) - ask(h[i]);
add(h[i], 1);
}
memset(c, 0, sizeof(c));
for (int i = n; i >= 1; i--)
{
rht[i] = ask(h[i] - 1);
add(h[i], 1);
}
long long ans = 0;
for (int i = 1; i <= n; i++)
{
//cout << lft[i] << ' ' << rht[i] << endl;
ans += 1ll * (1ll + lft[i] + rht[i]) * (lft[i] + rht[i]) / 2;
}
cout << ans;
}