計蒜客 排序
你需要分析排序演算法,將 n
nn 個互不相同的整數,通過交換兩個相鄰的元素使得數列有序的 最少交換次數。
比如,原數列為: 9,1
,0,5
,49,1,0,5,4
9,1,0,
5,4 排序後的數列為: 0,1
,4,5
,90,1,4,5,9
0,1,4,
5,9。
輸入格式
第一行乙個整數 n(n
≤500000
)n(n\leq500000)
n(n≤50
0000
)。接下來 n
nn 行,每行乙個整數 ai(
ai≤1
09)a_i(a_i\leq10^9)
ai(ai
≤109)。
591054
輸出格式
輸出乙個整數,表示操作次數。
6
樹狀陣列
離散化這是一道用樹狀陣列做的題,還要用到離散化的技巧。
一次有效的交換意味著什麼呢?
為了使序列有序,一次有效的交換應該是後乙個較小的數與他前乙個較大的數交換,那麼單獨乙個數字的交換次數,應該是這個數字前面比它大的數字的個數。
換句話說,當乙個數字出現的時候,出現在它左邊且比它大的數字的個數,就是當出現到這個數字為止(這個數字右邊的數字還沒出現)時,這個數字要到正確位置上交換的次數。
對每乙個位置上的數字累加這樣的次數,就是答案。
更一般的,令初始sum = 0
,對於第 i 個數字,出現在它左邊且比它大的數字的個數如果是 x,就令sum += x
,遍歷所有的數字,最後得到的累加和,就是答案。
現在剩乙個問題:怎麼知道出現在左邊的、比自己大的數字有多少?
對於遍歷到的第 i 個數字(記為 x),如果在遇到它的時候,單點更新樹狀陣列change(x)
,改變的動作是加 1,即計數,即令 x 出現的次數加 1,那麼,對於getsum(x)
操作,就是求出從 1 到 x 的數字一共出現了多少次,即,不小於 x 的數字有多少個。
當我們得知了這個結果以後,由於出現在 x 右邊的數字是還沒有遇到的,所以,不小於 x 的數字都出現在 x 的左邊,那就可以簡單地使用i + 1 - getsum(x)
得到出現在 x 左邊、比自己大的數字的個數。
這個公式是顯然成立的:對於第 i 個數字,目前已經出現的所有數字一共是 i + 1 個,在這些數字裡,不小於 x 的有getsum(x)
個,所以,大於 x 的有i + 1 - getsum(x)
個。並且,這些數字要麼是 x 本身,要麼出現在 x 的左邊。
累加所有的i + 1 - getsum(x)
,得到答案。
scanf
("%d"
,&n)
;for
(int i =
0; i < n; i++
)printf
("%d"
, ans)
;
但是,由於數字範圍實在是太大了,我們需要用到離散化的技巧,即將大範圍的數字對映到小範圍,因為我們只關心數字之間值的大小關係,而不關心具體的數值。
for
(int i =
0; i < n; i++
)
對於 c++,可以使用unique()
函式來去重,首先對dis
陣列排序,然後去重,得到去重後的長度length
。在去重後的陣列中,用二分查詢x[i]
所在的位置,並用這個位置作為x[i]
離散化後的值。
sort
(dis, dis + n)
;// 對資料排序
int length =
unique
(dis, dis + n)
- dis;
// 利用 unique 去重,使大小與下標對應,並得到去重後的長度
for(
int i =
0; i < n; i++
)
之後的操作與之前類似,遍歷 x 陣列,change(x[i])
,並計算ans += i + 1 - getsum(x[i])
。
最後要注意一下,ans
是long long
範圍的。
以及提一句,unique
和lower_bound
返回值都是long long int
,直接降級到int
是一種不好的做法。
#include
using
namespace std;
int n =0;
const
int max_n =
500007
;int c[max_n]=;
// 樹狀陣列
int x[max_n]=;
// 記錄原始資料
int dis[max_n]=;
// 離散化資料
long
long ans =0;
intlowbit
(int x)
intgetsum
(int x)
return res;
}void
change
(int x)
}int
main()
sort
(dis, dis + n)
;// 對資料排序
int length =
unique
(dis, dis + n)
- dis;
// 利用 unique 去重,使大小與下標對應,並得到去重後的長度
計蒜客 排序
大家知道,給出正整數 nn,則 11 到 nn 這 nn 個數可以構成 n n 種排列,把這些排列按照從小到大的順序 字典順序 列出,如 n 3n 3 時,列出 text1 2 3,1 3 2,2 1 3,2 3 1,3 1 2,3 2 1 六個排列。蒜頭君給出某個排列,求出這個排列的下 kk 個排...
計蒜客 字母排序
題目描述 例如我們正常的字母排列順序是abcdefg xyz,代表a b c x y z abcd efg hhh ihg四個字串的最長不降子串行的長度分別為4 3 3 1 輸入格式 第1,2行為字串含義如題描述 輸出格式 輸出答案含義如題描述 1 第二行長度 255 樣例輸入 abcdefghij...
計蒜客 成績排序
計蒜客 成績排序 小蒜給出了班裡某門課程的成績單,請你按成績從高到低對成績單排序輸出,如果有相同分數則名字字典序小的在前。輸入格式 第一行為 n 0 n 20 表示班裡的學生數目 接下來的 n行,每行為每個學生的名字和他的成績,中間用單個空格隔開。名字只包含字母且長度不超過 20,成績為乙個不大於 ...