【Codeforces】#658 C2.Prefix Flip(Hard Version)

C2.Prefix Flip (Hard Version)

题意

定义一种操作:
给出一个 01 串 s,现在可以选择一个前缀,将 01 翻转,并且将整个串倒过来。
现在给出两个串 s,t,问最少需要多少次操作使得 s = t。

思路

从后依次向前,依次使得\(s_i = t_i\)
当前 \(s_i != t_j\) :
\(s_0 != t_j\) :
对前缀 i 执行依次操作,现在 \(s_i = s_j\)
\(s_0 = t_j\):
先对前缀 1 执行一次操作使得\(s_0 != t_j\)
再按照上一步执行

思路就是这样,但是发现长度太大,不可能真的去 01 变换,进行翻转。

对于当前 i 我们无非就是要知道,现在到底是谁在这个位置?而且这个位置 01 变换了几次?

关于 01变换:

假如在下标 i 之前有 num 个位置进行了变换,那么下标 i 的变换次数最少为 num 次。要加上前缀 1 的变换对当前位置的影响。

关于翻转:
如果现在长度为 6,分别在 6、5、4、3位置发生了变换,不考虑前缀 1 的变换。
假如此时只是 6 位置发生了翻转,那么第 i 个位置代表的真正的字符是 \(s[7 - i]\)
此时 6、5位置发生了翻转,那么第 i 个位置代表的真正的字符是 \(s[7-6+i]\)
此时 6、5、4位置发生了翻转,那么第 i 个位置代表的真正的字符是 \(s[7-6+5-i]\)

此时6、5、4、3的位置发生了翻转,那么第 i 个位置代表的真正的字符是 \(s[7-6+5-4+i]\)

现在就可以发现规律了。

我们定义 \(op[i]\) 表示 s 串第 i 个位置额外翻转的次数,使用real(i)表示下标 i 表示的字符在 s 中的下标,那么 num + op[real(i)] 就是这个位置翻转的次数,再根据 \(s[real(i)]\) 就可以知道当前的字符是什么了。

在根据上面的步骤运算就可以了。

代码

/*
 * @Autor: valk
 * @Date: 2020-07-17 16:50:40
 * @LastEditTime: 2020-08-17 17:02:13
 * @Description: 如果邪恶 是 华丽残酷的乐章 它的终场 我会亲手写上 晨曦的光 风干最后一行忧伤 黑色的墨 染上安详
 */

#include <bits/stdc++.h>
#define fuck system("pause")
#define emplace_back push_back
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const int seed = 12289;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int N = 2e5 + 10;

char s[N], t[N];
int op[N], pre, num;
vector<int> ans;
char get(int real)
{
    int time = num + op[real];//这个位置的字符变换次数
    char c = s[real];
    if (time % 2) {
        c = (c == '1') ? '0' : '1';
    }
    return c;
}
int main()
{
    int _;
    scanf("%d", &_);
    while (_--) {
        memset(op, 0, sizeof(op));
        ans.clear();
        int n;
        scanf("%d%s%s", &n, s + 1, t + 1);
        pre = num = 0;
        for (int i = n; i > 1; i--) {
            int real = num % 2 ? pre - i : pre + i;// 第 i 个位置的在 s 中的位置
            char c = get(real);//第 i 个位置的字符
            if (c == t[i])//不需要变换
                continue;

            real = num % 2 ? pre - 1 : pre + 1;//第 1 个字符的位置
            c = get(real);//第 1 个位置的字符
            if (c == t[i]) {//相等要额外对前缀 1 进行一次变换
                ans.pb(1);
                op[real]++;//该位置操作次数增加
            }
            ans.pb(i);//不相等直接变换
            if (num % 2)
                pre -= i + 1;
            else
                pre += i + 1;
            num++;
        }
        int real = num % 2 ? pre - 1 : pre + 1;
        char c = get(real);
        if (c != t[1]) {
            ans.pb(1);
            num++;
        }
        printf("%d ", ans.size());
        for (int v : ans) {
            printf("%d ", v);
        }
        printf("\n");
    }
    return 0;
}
posted @ 2020-08-17 17:45  Valk3  阅读(101)  评论(0编辑  收藏  举报