CF1615F LEGOndary Grandmaster 题解
这题于看似简单的思路与代码中,蕴含着好几个美妙的技巧。下面就由我来一一解说。如果理解了这些小技巧,不仅能轻松理解这题思路,还能在做到类似题目时触类旁通,举一反三。
技巧一:操作的转化
原题中的取反操作要求两个连续的数都相同,那么我们可以将奇数位上的数取反,然后转化为交换操作。交换操作有一个特点,就是可以保证交换的两个数不相等,对应原字符串中就是两个数相等,这样很巧妙的满足了这个条件。
技巧二:次数的计算
首先,很明显推到这里,少了一个限制,这种题目的计算公式已经不会很复杂了。设
技巧三:利用贡献法。
讲到这里,我想最后一步也十分简单了。对于每个
#include <bits/stdc++.h>
using namespace std;
const int P = 1e9 + 7, N = 2005;
typedef long long LL;
int n;
LL suf[N][N << 1], pre[N][N << 1];
char a[N], b[N];
bool equal(char a, int b) {
return a == '?' || a == b + 48;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%s%s", &n, a + 1, b + 1);
for (int i = 0; i <= n + 1; ++i)
for (int j = 0; j <= 2 * n; ++j)
suf[i][j] = pre[i][j] = 0;
for (int i = 1; i <= n; ++i)
if (a[i] != '?' && (i & 1)) a[i] = (a[i] ^ 48 ^ 1) + 48;
for (int i = 1; i <= n; ++i)
if (b[i] != '?' && (i & 1)) b[i] = (b[i] ^ 48 ^ 1) + 48;
pre[0][n] = 1;
for (int i = 0; i < n; ++i)
for (int j = 0; j <= 2 * n; ++j)
for (int k = 0; k <= 1; ++k)
for (int p = 0; p <= 1; ++p)
if (equal(a[i + 1], k) && equal(b[i + 1], p) && j + k - p >= 0) (pre[i + 1][j + k - p] += pre[i][j]) %= P;
suf[n + 1][n] = 1;
for (int i = n + 1; i >= 2; --i)
for (int j = 0; j <= 2 * n; ++j)
for (int k = 0; k <= 1; ++k)
for (int p = 0; p <= 1; ++p)
if (equal(a[i - 1], k) && equal(b[i - 1], p) && j + k - p >= 0) (suf[i - 1][j + k - p] += suf[i][j]) %= P;
LL ans = 0;
for (int i = 1; i < n; ++i)
for (int j = -n; j <= n; ++j)
(ans += pre[i][j + n] * suf[i + 1][n - j] % P * abs(j) % P) %= P;
printf("%lld\n", ans);
}
}