Codeforces 1615F. LEGOndary Grandmaster (2800)
给定两个长度一样的 \(01\) 串 \(s,t\),其中有些位置上的字符已经忘记了,忘记的位置上是
?
。
定义 \(f(s,t)\) 表示最少使得 \(s=t\) 的操作数。操作是选择 \(s\) 中相邻的两个一样的字符,同时进行反转,即00
变11
,11
变00
。
计算出对于所有将 \(s,t\) 中的?
填成0
或1
的方案,令 \(s\) 填完后的字符串为 \(s'\),\(t\) 填完后的字符串为 \(t'\),所有 \(f(s',t')\) 的总和,答案对 \(10^9+7\) 取模。
\(2\le |s|\le 2000\)。
考虑对于填完的两个字符串 \(s,t\),计算使得 \(s=t\) 的最小操作数。
\(\color{purple}{\text{Trick:}}\)
由于一次操作恰好反转一个奇数位上的字符和一个偶数位上的字符,那么考虑将所有的奇数位上的字符反转。
此时反转原序列中的两个相邻的相同的字符,相当于在现序列上交换这两个字符;原序列中的两个不相邻的字符在现序列中是两个一样的字符,交换也不会影响。所以可以把原序列的操作转换为现序列的交换两个相邻字符。
那么现在若对 \(s,t\) 都进行此变换,得到的结果字符串为 \(s',t'\)。记 \(s'\) 中的 1
的位置是 \(p_1<p_2<\cdots <p_k\),\(t'\) 中的 1
的位置是 \(q_1<q_2<\cdots <q_k\),则最小操作数为 \(\sum\limits_{i=1}^{k} |p_i-q_i|\)。可以直接设 \(\text{dp}\) 解决,但其实还可以进一步化简。
考虑将 \(|p_i-q_i|\) 分到 \([\min(p_i,q_i),\max(p_i,q_i))\) 上,那么此时形成的序列即是 \(s'\) 的前缀 1
数量构成的序列与 \(t'\) 的前缀 1
数量构成的序列的差值。也就是记 \(a_i\) 为 \(s'[1...i]\) 中 1
的数量,\(b_i\) 为 \(t'[1...i]\) 中 1
的数量,答案就是 \(\sum\limits_{i=1}^{n} |a_i-b_i|\),而 \(|a_i-b_i|\) 每次的变化量 \(\Delta\in \{-1,0,1\}\),所以直接 \(\text{dp}\) 即可。
设 \(pre_{i,j}\) 表示确定了 \(s',t'\) 中的 \([1...i]\) 位置,\(\sum\limits_{k=1}^{i} (a_k-b_k)\) 的值为 \(j\) 的方案数,\(suf_{i,j}\) 表示确定了 \([i...n]\) 位置,\(\sum\limits_{k=i}^{n} (a_k-b_k)\) 的值为 \(j\) 的方案数。则答案为 \(\sum\limits_{i=1}^{n}\sum\limits_{\Delta =-i}^{i} |\Delta|\cdot pre_{i,\Delta}\cdot suf_{i+1,-\Delta}\)。
总时间复杂度 \(O(n^2)\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2005, D = 2001, mod = 1e9 + 7;
int T, n, ans, pre[N][N + N], suf[N][N + N]; char s[N], t[N];
inline bool check(char id, int c) { return (id == '?' || id == c + 48); }
inline void solve() {
scanf("%d%s%s", &n, s + 1, t + 1), ans = 0;
for (int i = 0; i <= n + 1; ++ i)
for (int j = 0; j < N * 2; ++ j)
pre[i][j] = suf[i][j] = 0;
pre[0][D] = suf[n + 1][D] = 1;
for (int i = 0; i < n; ++ i)
for (int j = -i; j <= i; ++ j) if (pre[i][j + D])
for (int x = 0; x < 2; ++ x) for (int y = 0; y < 2; ++ y)
if (check(s[i + 1], x ^ (i & 1)) && check(t[i + 1], y ^ (i & 1)))
(pre[i + 1][j + D + x - y] += pre[i][j + D]) %= mod;
for (int i = n + 1; i > 1; -- i)
for (int j = i - n - 1; j <= n - i + 1; ++ j) if (suf[i][j + D])
for (int x = 0; x < 2; ++ x) for (int y = 0; y < 2; ++ y)
if (check(s[i - 1], x ^ (i & 1)) && check(t[i - 1], y ^ (i & 1)))
(suf[i - 1][j + D + x - y] += suf[i][j + D]) %= mod;
for (int i = 1; i <= n; ++ i) for (int j = -i; j <= i; ++ j)
(ans += (ll)pre[i][j + D] * suf[i + 1][D - j] % mod * abs(j) % mod) %= mod;
printf("%d\n", ans);
}
int main() { for (scanf("%d", &T); T --; ) solve(); }