luogu p1966 火柴排序

2021-10-19 04:20:03 字數 2709 閱讀 5842

傳送門

給定兩個長度都為 \(n\) 的序列,求交換兩個陣列中相鄰數,使得 \(\sum_^n(a_i-b_i)^2\) 最小化的最小步數。

這題分為兩個部分,

那咱們先解決第乙個部分再來解決第二個部分:

看這個式子覺得毫無頭緒,不如咱們用完全平方差拆開一下這個式子:

\[\begin & \sum_^n(a_i-b_i)^2 \\ = & \sum_^n( ^ 2 -2a_ib_i+ ^ 2) \\ = & \sum_^n ^ 2 -\sum_^n2a_ib_i + \sum_^n ^ 2 \\ = & \sum_^n ^ 2 + \sum_^n ^ 2 - 2\sum_^na_ib_i \end \]

顯然,無論怎麼調整 \(a\) 和 \(b\) 的順序,\(\sum_^n ^ 2 + \sum_^n ^ 2\) 總是定值。因此,題目要求的式子的值與 \(\sum_^na_ib_i\) 有關,也就是要讓這個式子取最大值。(注意是最大值,不是最小值。因為原式子中是減它)

有乙個定理,當 \(a\),\(b\) 中對於每乙個位置 \(i\) 都有 \(a_i\) 是 \(a\) 中第 \(k\) 小的,而 \(b_i\) 也是 \(b\) 中第 \(k\) 小的時,\(\sum_^na_ib_i\) 最大。(本題已經規定所有火柴高度都是正整數了)

簡證:(預設 \(a, b\) 所有數都是正整數,因為資料範圍已經體現)取 \(a_1 \le a_2, b_1 \le b_2\), 可得 \(a_2(b_2-b_1) \ge a_1(b_2-b_1)\)。把這個式子變一下形可以得到:\(a_1b_1+a_2b_2 \ge a_1b_2+a_2b_1\)。推廣這個結論到多個數便可以得到結論了。

好,現在我們已經知道最優的情況是怎麼排列了,讓同乙個位置的兩個陣列中的數在原陣列中的大小地位相同即可。那麼調換的最小步數又該怎麼求呢?我們進入第二部分:

我們可以考慮這樣一種做法,

估計你此刻一定有114514個問題要問,別急,蟹蟹舉個例子慢慢說。

就按照樣例1來吧:

2 3 1 4

3 2 1 4

那麼將 \(a, b\) 排序,此時位於第 \(i\) 個位置上的數就是地位為 \(i\) 的數了。

排序後進行處理:

2 3 1 4 -> 1 2 3 4 -> 原編號:3 1 2 4

3 2 1 4 -> 1 2 3 4 -> 原編號:3 2 1 4

牢記這個式子:q[a[i].num] = b[i].num;

可得q[a[1].num] = b[1].num -> q[3] = 3

q[a[2].num] = b[2].num -> q[1] = 2

q[a[3].num] = b[3].num -> q[2] = 1

q[a[4].num] = b[4].num -> q[4] = 4

q陣列的值分別為:2 1 3 4

q陣列中共有1個逆序對(2, 1)

因此答案為 1

好,現在你應該理解這個方法是什麼意思了,接下來我們來看為什麼這樣做是對的。

首先,我們來看逆序對的定義。

\(i < j, a_i > a_j\)

轉化為此題中的 \(q\) 陣列,就可以說a[i].num > a[j].num, b[i].num < b[j].num。這說明了什麼?我們以a為基準調整b,那麼b[i]和b[j]的順序就是反的,需要別過來。

還有乙個定理,如果要讓有 \(p\) 個逆序對的序列通過兩兩交換變成上公升序列,那麼需要操作 \(p\) 次。(可以從必有逆序對緊挨定理得出,每次交換緊挨的逆序對即可)

那麼就可以得出答案就和 \(q\) 陣列逆序對數量相等了。

如何求逆序對數量?

/*

* @author: crab-in-the-northeast

* @date: 2020-11-01 17:04:02

* @last modified by: crab-in-the-northeast

* @last modified time: 2020-11-01 17:04:02

*/#include #include #include const int maxn = 100005;

const int mod = (int)1e8 - 3;

struct match

}a[maxn], b[maxn];

long long ans = 0;

int q[maxn], tmp[maxn];

void merge_sort(int l, int r)

}while (i <= mid)

tmp[k++] = q[i++];

while (j <= r)

tmp[k++] = q[j++];

for (int i = l; i <= r; ++i)

q[i] = tmp[i];

return ;

}int main()

這種問題一看就是要分part討論的,要注意分part討論的題一定不要合在一起討論。不然你會亂!!

然後看到相鄰交換使得序列有序之類的,就要從逆序對數量這個角度考慮了。(雖然這種情況有點特殊)

luogu p1966 火柴排序

傳送門給定兩個長度都為 n 的序列,求交換兩個陣列中相鄰數,使得 sum n a i b i 2 最小化的最小步數。這題分為兩個部分,那咱們先解決第乙個部分再來解決第二個部分 看這個式子覺得毫無頭緒,不如咱們用完全平方差拆開一下這個式子 begin sum n a i b i 2 sum n 2 2...

luogu P1966 火柴排隊

做水題好爽.展開式子 就是最大化 2 a i b i 顯然令大的乘大的能最大化上式 相當於用最小步數把1序列轉化為2序列 對映後求逆序對.include include includeinline int read while c 9 c 0 x x 10 c 0 c getchar return ...

Luogu P1966 火柴排隊

這還是一道比較簡單的題目,稍微想一下就可以解決。終於有noip難度的題目了 首先我們看那個 ai bi 2的式子,發現這個的最小值就是排序不等式 所以我們只需要改變第一組火柴的順序,使它和第二組火柴相對應 即大的對大的,小的對小的 然後我們離散一下,找出每乙個數該去的位置 然後注意到這裡的交換方式,...