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;
}
原文地址:https://www.cnblogs.com/lipoicyclic/p/13955683.html