問題描述
n 個小朋友站成一排。現在要把他們按身高從低到高的順序排列,但是每次只能交換位置相鄰的兩個小朋友。
每個小朋友都有一個不高興的程度。開始的時候,所有小朋友的不高興程度都是0。
如果某個小朋友第一次被要求交換,則他的不高興程度增加1,如果第二次要求他交換,則他的不高興程度增加2(即不高興程度爲3),依次類推。當要求某個小朋友第k次交換時,他的不高興程度增加k。
請問,要讓所有小朋友按從低到高排隊,他們的不高興程度之和最小是多少。
如果有兩個小朋友身高一樣,則他們誰站在誰前面是沒有關係的。
分析:在n個序列中的a,左邊有x個比a高的,右邊有y個比a矮的則,a的交換次數爲x+y次(題意爲1到x+y的和 = (x+y)*(x+y+1)/2)。這就需要求逆序對。可以用樹狀數組求逆序對,樹狀數組能動態維護和查詢,時間複雜度O(NlogN)。好像歸併排序也能做,可以試一試。
方法一:樹狀數組,O(NlogN)
#include <cstdio>
#include <cstring>
const int N = 100000+5, H = 1000000+5;
int num[N], c[H],sum[H];
int lowBit(int x){
return x&-x;
}
//前綴和
int getSum(int x){
int res = 0;
for(int i = x; i; i -= lowBit(i))
res += c[i];
return res;
}
//更新操作
void add(int x,int d){
for(int i = x; i < H; i += lowBit(i))
c[i] += d;
}
int main(int argc, char** argv) {
int n;
scanf("%d",&n);
for(int i = 0; i < n; i++){ //左邊逆序對
scanf("%d",&num[i]);
add(num[i]+1, 1); //注意樹狀數組是從1開始的;
sum[i] += i+1 - getSum(num[i]+1);
}
memset(c,0,sizeof(c));
for(int i = n-1; i >= 0; i--){ //反過來就是右邊逆序對
add(num[i]+1,1);
sum[i] += getSum(num[i]);
}
long long ans = 0;
for(int i = 0; i < n; i++)
ans += (long long)(1 + sum[i]) * sum[i] / 2; //10^6*10^6在int中會溢出
printf("%lld\n",ans);
return 0;
}
輸入格式
輸入的第一行包含一個整數n,表示小朋友的個數。
第二行包含 n 個整數 H1 H2 … Hn,分別表示每個小朋友的身高。
輸出格式
輸出一行,包含一個整數,表示小朋友的不高興程度和的最小值。
樣例輸入
3
3 2 1
樣例輸出
9
樣例說明
首先交換身高爲3和2的小朋友,再交換身高爲3和1的小朋友,再交換身高爲2和1的小朋友,每個小朋友的不高興程度都是3,總和爲9。
數據規模和約定
對於10%的數據, 1<=n<=10;
對於30%的數據, 1<=n<=1000;
對於50%的數據, 1<=n<=10000;
對於100%的數據,1<=n<=100000,0<=Hi<=1000000。