【题解】P10280 [USACO24OPEN] Cowreography G

· · 题解

link

题意

给定两个 01 串,每次操作可以将距离不超过 k 的两个字符交换,求 s\to t 的最小步数。

# 题解 考虑交换两个数,步数显然为 $\lceil\frac{y-x}{k}\rceil$。若没有取整,那么直接顺着扫过去,找到前后不一样的贪心选取即可,即每找到一个不一样的就马上与前面的任意一个交换。 容易感受到需要让取整时补出来的距离尽量少,也就是让每次的 $\lceil\frac{y-x}{k}\rceil k-(y-x)$ 尽量小。 当一个位置为 $i$ 的数需要交换时,选取一个 $j\equiv i\pmod k$ 的数是最优的,$j\equiv i+1\pmod k$ 次之,以此类推。 于是用 `set` 维护等待交换的位置,按 $i\bmod k$ 排序。如果无法交换就把当前位置塞进去。如果可以交换,查找最优的位置是简单的,计算答案也是简单的。 时间复杂度 $O(n\log n)$。 # 实现 ```cpp #include<bits/stdc++.h> using namespace std; typedef long long LL; const int N = 1000005; int n, k; LL ans = 0; char a[N], b[N]; #define mp make_pair multiset<pair<int, int>> s[2]; int main() { scanf("%d%d%s%s", &n, &k, a + 1, b + 1); for (int i = 1; i <= n; i++) if (a[i] != b[i]) { int x = a[i] - '0'; if (s[!x].empty()) s[x].insert(mp(i % k, i)); else { auto it = s[!x].lower_bound(mp(i % k, 0)); if (it == s[!x].end()) it = s[!x].begin(); ans += (i - (it -> second) - 1) / k + 1, s[!x].erase(it); } } printf("%lld\n", ans); return 0; } ```