• 【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("
    ");
        }
        return 0;
    }
    
  • 相关阅读:
    单例模式
    简单的WPS二次开发脚本
    使用DevExpress改变WinForm皮肤(VS)
    步入DevExpress的使用(VS)
    设置PdfPTable与标题间的距离
    tar: Removing leading `/’ from member names
    MySQL关闭过程详解和安全关闭MySQL的方法
    Nikto是一款Web安全扫描工具,可以扫描指定主机的web类型,主机名,特定目录,cookie,特定CGI漏洞,XSS漏洞,SQL注入漏洞等,非常强大滴说。。。
    解决Centos关闭You have new mail in /var/spool/mail/root提示
    hping3命令
  • 原文地址:https://www.cnblogs.com/valk3/p/13518958.html
Copyright © 2020-2023  润新知