CF1659B Bit Flipping
只考虑 $x$ 取反 $k$ 次的时候($x$ 的取值为 $0/1$):
-
若 $x \equiv{k}\pmod{2}$,则 $x$ 取反 $k$ 次后会是 $0$;
-
若 $x \not\equiv{k}\pmod{2}$,则 $x$ 取反 $k$ 次后会是 $1$。
因为高位为 $1$,答案会更优。
所以从高位往下操作 $c_i$:
-
若 $c_i$ 操作 $k$ 次取反后为 $0$,在 一次 中选取这一位当做定点(不取反),其他通通取反,最后 $c_i$ 还是 $1$,
-
若 $c_i$ 操作 $k$ 次取反后为 $1$,什么都不用管。
注意:“选取一个数为定点”这个操作是有限的,
-
若在操作的过程中,达到了限制 $k$,那么将没有扫描的部分取反 $k$ 次即可。
-
若操作完还没到达限制 $k$,剩下的全部将最后一位当做定点(因为尽量不要使高位变化)。
由于第二种考虑到最后一位,在处理的过程中最后一位单独处理。
讲的有点不清楚,具体看代码
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 2e5 + 10;
char c[N];
int ans[N];
int main() {
int T; scanf ("%d", &T);
while (T--) {
int n, k;
scanf ("%d%d%s", &n, &k, c + 1);
int cnt = 0, i; // i 记录当前处理到第几位,cnt 为操作了多少次
for (int j = 1; j <= n; ++j) ans[j] = 0;
for (i = 1; i < n && cnt < k; ++i) {// 顺着枚举是因为字符串出入时是顺着读进来的
ans[i] = 0;
if ((c[i] - '0') % 2 == k % 2) { // c[i] 取反 k 次后会为 0
ans[i] = 1; cnt ++; // 将 i 操作一次
}
c[i] = '1'; // 能处理的高位部分都会变成 1
}
if (i < n) { // 第二次分类的第一种情况。
while (i <= n) {
if (k & 1) c[i] ^= 1; ++i; // 若 k 为奇数,则取反 k 次相当于只取反 1 次
}
} else { // 此时的 i 为 n
if (cnt & 1) c[i] ^= 1;
ans[i] = k - cnt; // 不动 k - cnt 次,动 cnt 次
}
for (int i = 1; i <= n; ++i) printf ("%c", c[i]);
printf ("\n");
for (int i = 1; i <= n; ++i) printf ("%d ", ans[i]);
printf ("\n");
}
return 0;
}