CF1659B Bit Flipping

只考虑 $x$ 取反 $k$ 次的时候($x$ 的取值为 $0/1$):

  1. 若 $x \equiv{k}\pmod{2}$,则 $x$ 取反 $k$ 次后会是 $0$;

  2. 若 $x \not\equiv{k}\pmod{2}$,则 $x$ 取反 $k$ 次后会是 $1$。

因为高位为 $1$,答案会更优。

所以从高位往下操作 $c_i$:

  1. 若 $c_i$ 操作 $k$ 次取反后为 $0$,在 一次 中选取这一位当做定点(不取反),其他通通取反,最后 $c_i$ 还是 $1$,

  2. 若 $c_i$ 操作 $k$ 次取反后为 $1$,什么都不用管。

注意:“选取一个数为定点”这个操作是有限的,

  1. 若在操作的过程中,达到了限制 $k$,那么将没有扫描的部分取反 $k$ 次即可。

  2. 若操作完还没到达限制 $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;
}
posted @ 2022-04-20 15:50  wangzhongyuan  阅读(2)  评论(0编辑  收藏  举报  来源