zoukankan      html  css  js  c++  java
  • Codeforces Round #712 (Div. 2) B. Flip the Bits 题解

    旅行传送门:https://codeforces.com/contest/1504/problem/B

    B. Flip the Bits

    time limit per test
    1 second
    memory limit per test
    256 megabytes
    input
    standard input
    output
    standard output
    There is a binary string a of length n. In one operation, you can select any prefix of a with an equal number of 0 and 1 symbols. Then all symbols in the prefix are inverted: each 0 becomes 1 and each 1 becomes 0.
     
    For example, suppose a=0111010000.
     
    In the first operation, we can select the prefix of length 8 since it has four 0's and four 1's: [01110100]00→[10001011]00.
    In the second operation, we can select the prefix of length 2 since it has one 0 and one 1: [10]00101100→[01]00101100.
    It is illegal to select the prefix of length 4 for the third operation, because it has three 0's and one 1.
    Can you transform the string a into the string b using some finite number of operations (possibly, none)?
     
    Input
    The first line contains a single integer t (1 ≤ t ≤ 10 ^ 4) — the number of test cases.
     
    The first line of each test case contains a single integer n (1 ≤ n ≤ 3⋅10 ^ 5) — the length of the strings a and b.
     
    The following two lines contain strings a and b of length n, consisting of symbols 0 and 1.
     
    The sum of n across all test cases does not exceed 3⋅10 ^ 5.
     
    Output
    For each test case, output "YES" if it is possible to transform a into b, or "NO" if it is impossible. You can print each letter in any case (upper or lower).
     
    Example
    input
    Copy
    5
    10
    0111010000
    0100101100
    4
    0000
    0000
    3
    001
    000
    12
    010101010101
    100110011010
    6
    000111
    110100
    
    output
    Copy
    YES
    YES
    NO
    YES
    NO
    
    Note
    The first test case is shown in the statement.
     
    In the second test case, we transform a into b by using zero operations.
     
    In the third test case, there is no legal operation, so it is impossible to transform a into b.
     
    In the fourth test case, here is one such transformation:
     
    Select the length 2 prefix to get 100101010101.
    Select the length 12 prefix to get 011010101010.
    Select the length 8 prefix to get 100101011010.
    Select the length 4 prefix to get 011001011010.
    Select the length 6 prefix to get 100110011010.
    In the fifth test case, the only legal operation is to transform a into 111000. From there, the only legal operation is to return to the string we started with, so we cannot transform a into b.
     

    题目大意

    给你一个长度为n的二进制字符串a。 每次操作可以选择带有0和1字符数相等的a的任何前缀。 然后前缀中的所有符号都将反转:每个0变为1,每个1变为0。
    问你在经过若干次操作后,字符串a是否可以转换为字符串b。
     

    解题思路

    首先此题有几个要注意的地方:

    1.每次操作变换的是字符串a中长度为i的前缀(即是对字符串a的前i个字符取异或)

    2.每次操作选取的前缀中0和1的字符数相等

    试想一下,如果是模拟,我们应该怎么做?

    首先从末尾开始对两字符串进行逐个比较,如果ab第i个数字互异,则将a中长度为i且01字符数相等的前缀全部取异或,然后从当前位置继续比对,直至无法修改或a转换成b为止。显然,每次都修改并重新统计的话是行不通的,复杂繁琐的代码与极高的时间复杂度令人望而却步。

    那么我们先统计字符串ab中数字1的个数,然后反向处理字符串,用一个指针记录后缀相同的位置,因为每次修改后前缀中匹配与不匹配的关系就互换了,所以若是进行过奇数次修改,那就从修改处开始往前搜索两字符串最初匹配的部分直至遇到不匹配的;否则就搜索不匹配的部分。如果某次操作时选取的前缀中0和1的字符数不等或ab两字符串的前缀中0或1的数目不相等,说明a不可能转换为b,如果能一直修改到底,说明可以成功转换。

    笔者语文水平有限,可能文字说明比较晦涩难懂,以样例来作进一步阐释说明:

    string a = 010101010101

    string b = 100110011010

     

    先统计a、b中1的个数均为6

    从末尾向前搜索,长度为4的后缀不同,记录i的位置为8,第一次操作:[010101010101] → [101010101010]

    从i = 8的位置继续向前搜索,此时原字符串中以i为长度的前缀与b的匹配关系相反,读出原来7-8的字符匹配(奇数次操作后不匹配了),第二次操作:[10101010]1010→ [01010101]1010

    从i = 6的位置继续向前搜索,异或偶数次后原字符串中以i为长度的前缀又变回最初的样子,读出5-6的字符不匹配,第三次操作:[010101]011010[101010]011010

    从i = 4的位置继续向前搜索,奇数次,找原来匹配的3-4,第四次操作:[1010]10011010→ [0101]10011010

    最终操作:[01]1010011010→ [10]0110011010

    不难发现,上述操作选取的前缀中0和1的字符数都保持相等且ab两字符串的前缀中0或1的数目也相等

     

    再举两个反例吧:

    string a = 001

    string b = 000

    一开始统计后就能发现ab串中0、1数目不等,直接pass

     

    string a = 000111

    string b = 110100

    第一次操作后:a 111000

    从i = 4处往前搜索,奇数次找原来的匹配项,3-4相等,但此时两条件都不满足,说拜拜~

     

    剩下的看代码呗,如果有疑惑或者更好的作法欢迎在评论区与小蒟交流,文笔不足之处还请见谅(✿◡‿◡)

     

    AC代码

     1 #include <bits/stdc++.h>
     2 #define MAXN 300000 + 10
     3 
     4 char s1[MAXN], s2[MAXN];
     5 
     6 int main(int argc, char const *argv[])
     7 {
     8     int t;
     9     scanf("%d", &t);
    10     while (t--)
    11     {
    12         /**
    13          * sum1 a中字符1的数目 
    14          * sum2 b中字符1的数目
    15          * cnt 操作次数 
    16          */
    17         int n, sum1 = 0, sum2 = 0, flag = 1, cnt = 0;
    18         scanf("%d", &n);
    19         scanf("%s%s", s1 + 1, s2 + 1); //字符串偏移
    20         for (int i = 1; i <= n; i++)   //统计字符串ab中1的数目
    21         {
    22             sum1 += s1[i] - '0';
    23             sum2 += s2[i] - '0';
    24         }
    25         if (sum1 != sum2) //开局不相等就可以直接/remake了
    26         {
    27             puts("NO");
    28             flag = 0;
    29             continue;
    30         }
    31         for (int i = n; i; i--) //i作为字符串指针
    32         {
    33             if (s1[i] == s2[i])
    34             {
    35                 if (!(cnt % 2)) //偶数次异或操作还原,找不匹配的部分
    36                 {
    37                     sum1 -= (s1[i] - '0');
    38                     sum2 -= (s2[i] - '0');
    39                     continue;
    40                 }
    41                 else //奇数次操作匹配关系互换,找最初匹配的部分
    42                 {
    43                     while (s1[i] == s2[i] && i)
    44                     {
    45                         sum1 -= (s1[i] - '0');
    46                         sum2 -= (s2[i] - '0');
    47                         i--;
    48                     }
    49                     if (sum1 != sum2 || i - sum1 != sum1) //两个条件不满足其一都不能完成转换
    50                     {
    51                         puts("NO");
    52                         flag = 0;
    53                         break;
    54                     }
    55                     i++, cnt++; //之前的while循环指针往前多挪了一位
    56                 }
    57             }
    58             else
    59             {
    60                 while (s1[i] != s2[i] && i)
    61                 {
    62                     sum1 -= (s1[i] - '0');
    63                     sum2 -= (s2[i] - '0');
    64                     i--;
    65                 }
    66                 if (sum1 != sum2 || i - sum1 != sum1)
    67                 {
    68                     puts("NO");
    69                     flag = 0;
    70                     break;
    71                 }
    72                 i++, cnt++;
    73             }
    74         }
    75         if (flag)
    76             puts("YES");
    77     }
    78     return 0;
    79 }
    View Code
  • 相关阅读:
    欧拉回路 定理
    UESTC 1087 【二分查找】
    POJ 3159 【朴素的差分约束】
    ZOJ 1232 【灵活运用FLOYD】 【图DP】
    POJ 3013 【需要一点点思维...】【乘法分配率】
    POJ 2502 【思维是朴素的最短路 卡输入和建图】
    POJ 2240 【这题貌似可以直接FLOYD 屌丝用SPFA通过枚举找正权值环 顺便学了下map】
    POJ 1860【求解是否存在权值为正的环 屌丝做的第一道权值需要计算的题 想喊一声SPFA万岁】
    POJ 1797 【一种叫做最大生成树的很有趣的贪心】【也可以用dij的变形思想~】
    js 实现slider封装
  • 原文地址:https://www.cnblogs.com/Foreign/p/14638579.html
Copyright © 2011-2022 走看看