Description奶牛们组了一支舞蹈队,Farmer John 是她们的编舞!舞蹈队最新而最精彩的舞蹈有 N N N 头奶牛(2 ≤ N ≤ 1 0 6 2\le N\le 10^6 2 ≤ N ≤ 1 0 6 )排成一行。舞蹈中的每次动作都涉及两头奶牛,至多相距 K K K 个位置(1 ≤ K < N 1\le K < N 1 ≤ K < N ),优雅地跳起并降落在对方的位置上。
队伍中有两种奶牛——更赛牛(Guernsey)和荷斯坦牛(Holstein)。因此,Farmer John 将这一舞蹈记录为一系列长为 N N N 的 01 字符串 ,其中 0 代表更赛牛,1 代表荷斯坦牛,整个字符串表示奶牛在这一行中是如何排列的。
不幸的是,Farmer Nhoj(对手团队的编舞)蓄意破坏了这一舞蹈,并清除了除第一个和最后一个 01 字符串之外的所有内容!由于一场大型比赛即将开始,Farmer John 必须抓紧每一秒重建这一舞蹈。
给定这两个 01 字符串,帮助 Farmer John 求出舞蹈中的最小动作数量!
Solution考虑交换一对 i , j ( a i ≠ a j ) i,j(a_i\neq a_j) i , j ( a i = a j ) 所用的代价,结论是 ⌈ ∣ i − j ∣ k ⌉ \left\lceil\frac{|i-j|}{k}\right\rceil ⌈ k ∣ i − j ∣ ⌉ 。
证明就考虑数学归纳法,不妨设 i < j i<j i < j 且长度小于 j − i j-i j − i 的已经满足结论,容易发现对于 j − i ≤ k j-i\leq k j − i ≤ k 的 ( i , j ) (i,j) ( i , j ) 一定满足条件。
对于 i < s < j i<s<j i < s < j ,如果 a i = a s a_i=a_s a i = a s 则先交换 ( j , s ) (j,s) ( j , s ) 再交换 ( i , s ) (i,s) ( i , s ) ,否则先交换 ( i , s ) (i,s) ( i , s ) 在交换 ( j , s ) (j,s) ( j , s ) ,对应的代价为 ⌈ s − i k ⌉ + ⌈ j − s k ⌉ \left\lceil\frac{s-i}{k}\right\rceil+\left\lceil\frac{j-s}{k}\right\rceil ⌈ k s − i ⌉ + ⌈ k j − s ⌉ ,容易发现当 s s s 取 i + k i+k i + k 时可以取到最小值,即为 ⌈ ∣ i − j ∣ k ⌉ \left\lceil\frac{|i-j|}{k}\right\rceil ⌈ k ∣ i − j ∣ ⌉ 。
有了这个结论就可发现这里的交换一定是两两匹配,即在一张二分图上匹配。
如果没有取上整就从前往后扫,如果 a i ≠ b i a_i\neq b_i a i = b i 就找到任意一个还没匹配的 j j j 使得 a i ≠ a j a_i\neq a_j a i = a j 匹配,容易发现这个一定是最小值。
如果有了取上整,感性理解可以发现这里需要让 ( i − j ) m o d k (i-j)\bmod k ( i − j ) m o d k 的和尽可能小,所以只要把上面选任意一个匹配变为选使得 ( i − j ) m o d k (i-j)\bmod k ( i − j ) m o d k 最小的 j j j 匹配即可。
时间复杂度:O ( n log n ) O(n\log n) O ( n log n ) 。
Code1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <bits/stdc++.h> 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 ; while (T--) dickdreamer (); return 0 ; }