【牛逼题】CF1615F LEGOndary Grandmaster

Trick 1:原题中的取反操作要求两个连续的数都相同,那么我们可以将奇数位上的数取反,然后转化为交换操作。交换操作有一个特点,就是可以保证交换的两个数不相等,对应原字符串中就是两个数相等,这样很巧妙的满足了这个条件。

两个字符串可以被操作,当且仅当它们两长度相等且 \(1\) 的个数相等。

设第一个字符串出现 \(1\) 的位置为 \(x_{1\to k}\),第二个 \(y_{1\to k}\)

则我们有

\[ans=\sum_{i=1}^{k} |x_i-y_i| \]

发现比较困难。

于是

Trick 2:设 \(a_i,b_i\) 分别为两个字符串前 \(i\) 个字母中 \(1\) 的个数。于是我们有。

\[\sum_{i=1}^{k} |x_i-y_i|=\sum_{i=1}^{k} |a_i-b_i| \]

因为你每交换两个数,最多只能使一个前缀和发生变化。

Trick 3:对于每个 \(i\),我们枚举 \(j = a_i-b_i\),计算这个 \(j\) 的贡献。具体的,可以令 \(pre_{i, j}\) 表示前 \(i\) 个数使 $a_i - b_i = j $ 的方案数, \(suf_{i, j}\) 则表示相应的后缀的方案,那么 \((i,j)\) 的贡献就是 \(pre_{i, j} \times suf_{i+1,-j} \times |j|\)。并且经过转化之后,这个转移也变得十分简单。

注意第二维你得 \(+n\).


/*https://img2024.cnblogs.com/blog/2370016/202503/2370016-20250323083849219-1339968737.jpg*/
#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;
}
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] = 1-(a[i] ^ 48 );
			else if(a[i]!='?' )a[i]-='0'; 
		}
		for (int i = 1; i <= n; ++i){
			
			if (b[i] != '?' && (i & 1)) b[i] = 1-(b[i] ^ 48 );
			else if(b[i]!='?' )b[i]-='0';
		}
		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);
	}
}
posted @ 2025-03-21 20:19  houpingze  阅读(46)  评论(2)    收藏  举报