Description
奶牛們組了一支舞蹈隊,Farmer John 是她們的編舞!舞蹈隊最新而最精彩的舞蹈有 \(N\) 頭奶牛(\(2\le N\le 10^6\))排成一行。舞蹈中的每次動作都涉及兩頭奶牛,至多相距 \(K\) 個位置(\(1\le K < N\)),優雅地跳起並降落在對方的位置上。
隊伍中有兩種奶牛——更賽牛(Guernsey)和荷斯坦牛(Holstein)。因此,Farmer John 將這一舞蹈記錄為一系列長為 \(N\) 的 01
字串,其中 0
代表更賽牛,1
代表荷斯坦牛,整個字串表示奶牛在這一行中是如何排列的。
不幸的是,Farmer Nhoj(對手團隊的編舞)蓄意破壞了這一舞蹈,並清除了除第一個和最後一個 01
字串之外的所有內容!由於一場大型比賽即將開始,Farmer John 必須抓緊每一秒重建這一舞蹈。
給定這兩個 01
字串,幫助 Farmer John 求出舞蹈中的最小動作數量!
Solution
考慮交換一對 \(i,j(a_i\neq a_j)\) 所用的代價,結論是 \(\left\lceil\frac{|i-j|}{k}\right\rceil\)。
證明就考慮數學歸納法,不妨設 \(i<j\) 且長度小於 \(j-i\) 的已經滿足結論,容易發現對於 \(j-i\leq k\) 的 \((i,j)\) 一定滿足條件。
對於 \(i<s<j\),如果 \(a_i=a_s\) 則先交換 \((j,s)\) 再交換 \((i,s)\),否則先交換 \((i,s)\) 在交換 \((j,s)\),對應的代價為 \(\left\lceil\frac{s-i}{k}\right\rceil+\left\lceil\frac{j-s}{k}\right\rceil\),容易發現當 \(s\) 取 \(i+k\) 時可以取到最小值,即為 \(\left\lceil\frac{|i-j|}{k}\right\rceil\)。
有了這個結論就可發現這裡的交換一定是兩兩匹配,即在一張二分圖上匹配。
如果沒有取上整就從前往後掃,如果 \(a_i\neq b_i\) 就找到任意一個還沒匹配的 \(j\) 使得 \(a_i\neq a_j\) 匹配,容易發現這個一定是最小值。
如果有了取上整,感性理解可以發現這裡需要讓 \((i-j)\bmod k\) 的和儘可能小,所以只要把上面選任意一個匹配變為選使得 \((i-j)\bmod k\) 最小的 \(j\) 匹配即可。
時間複雜度:\(O(n\log n)\)。
Code
#include <bits/stdc++.h>
// #define int int64_t
int n, k;
std::string a, b;
void dickdreamer() {
std::cin >> n >> k >> a >> b;
a = " " + a, b = " " + b;
std::set<std::pair<int, int>> st[2];
int64_t ans = 0;
for (int i = 1; i <= n; ++i) {
if (a[i] == b[i]) continue;
int oa = a[i] - '0', ob = b[i] - '0';
if (!st[ob].empty()) {
auto it = st[ob].lower_bound({i % k, 0});
if (it == st[ob].end()) it = st[ob].begin();
ans += (i - it->second + k - 1) / k;
st[ob].erase(it);
} else {
st[oa].emplace(i % k, i);
}
}
std::cout << ans << '\n';
}
int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}