简要题意:给定两个 01
字符串 $a,b$,每次可以选择下标 $i,j$,其中 $|i-j|\le k$,交换 $a[i],a[j]$,求从 $a$ 变成 $b$ 的最小步数。
如果 $a[i]=b[i]$,直接跳过。否则有两种情况:$a[i]=0, b[i]=1$ 或 $a[i]=1, b[i]=0$。
如果有 $i,j$ 满足 $a[i]=0, b[i]=1, a[j]=1, b[j]=0$,则交换 $i,j$。这样的 $i,j$ 分别放在二分图的两边,构造出了二分图模型。考虑如何加速二分图匹配。
交换下标 $i,j(i<j)$ 的代价是 $\lceil \frac{j-i}{k} \rceil$,不考虑向上取整,那么最小的 $i$ 匹配最小的 $j$,次小的 $i$ 匹配次小的 $j$,以此类推,一定是最优策略。
再考虑有向上取整怎么办。一个显然的贪心策略是,使“浪费”尽量少,也就是说 $j\equiv i$ 是最优的,$j\equiv i+1$ 次优,等等。
考虑使用两个 set
维护二分图的两边,使用二分查找寻找最不“浪费”的匹配点。
// Title: Cowreography
// Source: USACO24OPEN Gold
// Author: Jerrywang
#include <bits/stdc++.h>
#define ll long long
#define rep(i, s, t) for(int i=s; i<=t; ++i)
#define debug(x) cerr<<#x<<":"<<x<<endl;
const int N=1000010;
using namespace std;
int n, k, a[N], b[N]; ll res;
set<pair<int, int>> S[2];
int main()
{
#ifdef Jerrywang
freopen("E:/OI/in.txt", "r", stdin);
#endif
scanf("%d%d", &n, &k);
rep(i, 1, n) scanf("%1d", a+i);
rep(i, 1, n) scanf("%1d", b+i);
rep(i, 1, n) if(a[i]!=b[i])
{
auto &S1=S[a[i]], &S2=S[b[i]];
if(S1.empty())
{
S2.insert({i%k, i}); continue;
}
auto it=S1.lower_bound({i%k, 0});
if(it==S1.end()) it=S1.begin();
int j=it->second;
res+=(i-j+k-1)/k; S1.erase(it);
}
printf("%lld", res);
return 0;
}
sz