\(link\)
涵涵有兩盒火柴,每盒裝有\(n\)根火柴,每根火柴都有乙個高度。 現在將每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 兩列火柴之間的距離定義為:\(\sum (a_i-b_i)^2\)
其中\(a_i\)表示第一列火柴中第\(i\)個火柴的高度,\(b_i\)表示第二列火柴中第\(i\)個火柴的高度。
每列火柴中相鄰兩根火柴的位置都可以交換,請你通過交換使得兩列火柴之間的距離最小。請問得到這個最小的距離,最少需要交換多少次?如果這個數字太大,請輸出這個最小交換次數對\(10^8-3\)取模的結果。
注意到\(\sum(a_i-b_i)^2=\sum+\sum-2\sum\)。而前面兩項都是不會改變的,所以只能考慮改變最後一項\(2\sum\)了。
因為我們要最小化\(\sum(a_i-b_i)^2\),所以要最大化\(\sum\)。現在給出乙個結論:順序積之和\(\ge\)無序積之和。這是為什麼呢?
我們考慮乙個子問題:設\(a_1,那麼\(a_1b_1+a_2b_2>a_1b_2+a_2b_1\)。證明就是直接移項,有\(a_2(b_2-b_1)>a_1(b_2-b_1)\),又\(b_1,所以\(a_2>a_1\),即\(a_1,證畢。
那麼對於兩個無序的序列\(a_i,b_i\),我們通過類似氣泡排序的思想,總能通過交換若干項,使總貢獻越來越大,最後總能變成兩個順序數列,使貢獻達到最大。
我們把原來的\(a_i,b_i\)離散化,那麼原問題就轉化成了另乙個問題:對於兩個\(1\sim\)的排列\(a_i,b_i\),試通過交換若干項,使得\(\forall,a_i=b_i\)。
這個問題也是比較好解決的。我們先把\(a_i\)轉化成順序數列,然後記錄一下\(to[a[i]]=i\),表示\(a_i\)這個數變成了\(i\)這個數。再把\(b_i\)進行相應轉化,即\(b_i=to[b_i]\)。則現在問題又變成另乙個更簡單的問題:對於乙個無序排列,最少要交換幾次,使它變成有序?
這其實就是乙個求逆序對啦。因為你要把\(1\sim\)這\(n\)個數從小到大依次交換到對應的位置\(a_i=i\)。通過手動模擬可以發現,數\(i\)移到\(a_i\)這個位置需要的交換次數就是\(\sum\limits_^|a_j>i|\)。那麼這就是求逆序對了。具體可以用樹狀陣列實現。
#include using namespace std;
#define lowbit(x) ((x) & (-x))
const int mod = 1e8 - 3;
int n, res, a[100005], b[100005], p[100005], q[100005], to[100005], c[100005];
int read()
while (ch >= '0' && ch <= '9')
return x * fl;
}void add(int x, int d)
return;
}int query(int x)
return s;
}int main()
for (int i = 1; i <= n; i ++ )
to[a[i]] = i;
for (int i = 1; i <= n; i ++ )
b[i] = to[b[i]];
for (int i = 1; i <= n; i ++ )
printf("%d\n", res);
return 0;
}
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的式子,發現這個的最小值就是排序不等式 所以我們只需要改變第一組火柴的順序,使它和第二組火柴相對應 即大的對大的,小的對小的 然後我們離散一下,找出每乙個數該去的位置 然後注意到這裡的交換方式,...
luogu P1966 火柴排隊 離散,逆序對
每盒裝有 其中每列火柴中相鄰兩根火柴的位置都可以交換,請你通過交換使得兩列火柴之間的距離最小。請問得到這個最小的距離,最少需要交換多少次?如果這個數字太大,請輸出這個最小交換次數對 由於交換 對於特定的兩組 對應元素排名相同.故先離散,再求逆序對即為答案 可以通過歸併或樹狀陣列求解 include ...