Reversi 3

Reversi 3

0/1字符串翻转操作,差分

问题描述

给定两个长度为 \(N\) 的 0/1 字符串 \(A\) 和 \(B\)。
可以执行任意次以下操作:

选择整数 \(i\)(\(2 \le i \le N-1\)),满足 \(A_{i-1} = A_{i+1}\),然后翻转 \(A_i\)(0 变 1,1 变 0)。

判断能否将 \(A\) 变成 \(B\),若能,求最少操作次数。


1. 差分序列与操作转化

定义差分序列 \(D\),长度为 \(N-1\):

\[D_j = A_j \oplus A_{j+1} \quad (1 \le j \le N-1) \]

其中 \(\oplus\) 表示异或运算。

  • 若 \(A_j = A_{j+1}\),则 \(D_j = 0\);
  • 若 \(A_j \neq A_{j+1}\),则 \(D_j = 1\)。

翻转 \(A_i\) 会同时影响 \(D_{i-1}\) 和 \(D_i\),使它们分别取反。

操作条件 \(A_{i-1} = A_{i+1}\) 等价于:

\[D_{i-1} = D_i \]

因此,在差分序列上,操作变为:
选择相邻且相等的两个 \(D\) 值,将它们同时取反。


2. 交替前缀和 \(P\)

定义交替前缀和序列 \(P\):

\[P_0 = 0, \qquad P_k = \sum_{j=1}^{k} (-1)^j D_j \quad (1 \le k \le N-1) \]

注意 \((-1)^j\) 是交替符号:\(j=1\) 时为 \(-1\),\(j=2\) 时为 \(+1\),依此类推。

由于 \(D_j \in \{0,1\}\),所以 \(P_k\) 是整数。

一次操作对 \(P\) 的影响

一次操作同时翻转 \(D_{i-1}\) 和 \(D_i\),且原来 \(D_{i-1} = D_i\)。
在 \(P\) 序列中:

  • 对于 \(k < i-1\),\(P_k\) 不变;
  • \(P_{i-1}\) 会增加或减少 \(1\);
  • 对于 \(k \ge i\),由于 \(D_{i-1}\) 和 \(D_i\) 的系数 \((-1)^{i-1}\) 与 \((-1)^i\) 互为相反数,变化量正好抵消,所以 \(P_k\) 不变。

结论:一次操作恰好只改变一个内部点 \(P_{i-1}\),使其值 \(\pm 1\),其他所有 \(P\) 值保持不变。

操作条件在 \(P\) 上表现为:

\[P_{i-2} = P_i \]


3. 目标映射与不变量

对目标串 \(B\) 做相同的映射,得到差分 \(E_j = B_j \oplus B_{j+1}\) 和交替前缀和 \(Q_0, Q_1, \dots, Q_{N-1}\)。

  • 首尾字符无法通过操作改变,因此必须满足:

    \[A_1 = B_1, \qquad A_N = B_N \]

    否则无解。

  • \(P_{N-1}\) 是操作下的不变量,因此必须满足:

    \[P_{N-1} = Q_{N-1} \]

    否则无解。

在满足以上条件时,\(P_0 = Q_0 = 0\),\(P_{N-1} = Q_{N-1}\) 已经固定。
我们需要将中间的每个 \(P_j\)(\(1 \le j \le N-2\))调整为 \(Q_j\)。


4. 最少操作次数

每次操作只能让一个内部点 \(P_j\) 改变 \(1\)。
要把 \(P_j\) 变成 \(Q_j\),至少需要 \(|P_j - Q_j|\) 次操作。
这些内部点可以独立调整(通过合理安排操作顺序,总能满足操作条件),因此总操作次数为:

\[\boxed{\text{ans} = \sum_{j=1}^{N-2} |P_j - Q_j|} \]


5. 算法步骤

  1. 检查 \(A_1 = B_1\) 且 \(A_N = B_N\),若不满足则输出 \(-1\)。
  2. 初始化 \(p = 0, q = 0, \text{ans} = 0\)。
  3. 遍历 \(j = 1\) 到 \(N-1\):
    • 计算 \(d = A_j \oplus A_{j+1}\),\(e = B_j \oplus B_{j+1}\)。
    • 计算符号 \(\text{sgn} = (-1)^j\)。
    • 更新 \(p \leftarrow p + \text{sgn} \cdot d\),\(q \leftarrow q + \text{sgn} \cdot e\)。
    • 若 \(j < N-1\),则 \(\text{ans} \leftarrow \text{ans} + |p - q|\)。
  4. 若 \(p \neq q\),输出 \(-1\);否则输出 \(\text{ans}\)。

时间复杂度 \(O(N)\),总 \(N\) 不超过 \(10^6\)。


6. 正确性说明

  • 可达性:首尾相同且 \(P_{N-1} = Q_{N-1}\) 是充要条件(由不变量决定)。
  • 最小性:每次操作只能改变一个内部点 \(\pm 1\),下界为 \(\sum |P_j - Q_j|\);通过从左到右依次调整,可以达到该下界。

7. 具体例子

取 \(N = 5\),
\(A = 0\,1\,0\,1\,0\)
\(B = 0\,0\,0\,0\,0\)

对 A 计算 D 和 P

j A_j A_ D_j (-1)^j (-1)^j D_j P_j
0 P0=0
1 0 1 1 -1 -1 P1=-1
2 1 0 1 +1 +1 P2=0
3 0 1 1 -1 -1 P3=-1
4 1 0 1 +1 +1 P4=0

得到 \(P = [0, -1, 0, -1, 0]\)。

对 B 计算 Q

\(B = 0\,0\,0\,0\,0\),所有 \(E_j = 0\),所以 \(Q = [0,0,0,0,0]\)。

检查条件

首:\(A_1 = 0 = B_1\) ✓
尾:\(A_5 = 0 = B_5\) ✓
\(P_4 = 0 = Q_4\) ✓

计算答案

内部点 \(j = 1,2,3\):

\[|P_1 - Q_1| = |-1 - 0| = 1 \]

\[|P_2 - Q_2| = |0 - 0| = 0 \]

\[|P_3 - Q_3| = |-1 - 0| = 1 \]

总操作次数 \(= 1 + 0 + 1 = 2\)。

对应真实操作

初始 \(A = 0\,1\,0\,1\,0\)

操作 1:选 \(i=2\)。检查 \(A_1=0, A_3=0\),相等。翻转 \(A_2\):\(1 \to 0\)。
得到 \(A = 0\,0\,0\,1\,0\)。
此时 \(P_1\) 从 \(-1\) 变为 \(0\)。

操作 2:选 \(i=4\)。检查 \(A_3=0, A_5=0\),相等。翻转 \(A_4\):\(1 \to 0\)。
得到 \(A = 0\,0\,0\,0\,0\),等于 \(B\)。
此时 \(P_3\) 从 \(-1\) 变为 \(0\)。

最终 \(P = [0,0,0,0,0] = Q\),与答案一致。


#include <bits/stdc++.h>
using namespace std;
using ll = long long;

void solve() {
    int n; string A, B;
    cin >> n >> A >> B;

    // 首尾字符必须相同
    if(A[0] != B[0] || A[n - 1] != B[n - 1]) {cout << "-1\n";return;}

    ll p = 0, q = 0;   // 交替前缀和
    ll ans = 0;

    // 遍历所有相邻对 (j 从 1 到 N-1)
    for(int i = 0; i < n - 1; i ++) {
        // 差分值
        int d = (A[i] ^ A[i+1]) & 1;   // 0/1
        int e = (B[i] ^ B[i+1]) & 1;

        int sign = (i % 2 == 0) ? -1 : 1;   //交替和
        p += sign * d;
        q += sign * e;

        // 只有内部点(j=1..n-2)才可以改变
        if(i < n - 2) ans += abs(p - q);
    }

    if(p != q) cout << "-1\n";
    else cout << ans << "\n";
}

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr);
    int _=1; cin >> _;
    while(_--) solve();
    return 0;
}
posted @ 2026-11-03 21:31  thenrify  阅读(3)  评论(0)    收藏  举报