歷屆試題 小朋友排隊 逆序對(樹狀數組)

  題目鏈接                  藍橋杯---歷屆真題 題解

問題描述

  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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章