题目大意:有黑球和白球排成两行,每次可以从任意一行排头取一个球放到输出序列尾部。设得到第 \(i\) 种输出序列的方案数为 \(a_i\),求 \(\sum a_i^2\)
dp、计数
分析:
根据乘法原理,方案数的平方和,等于两个人独立地进行操作,然后他们得到相同输出序列的方案数。
那么我们就可以暴力设一个状态 \(f[i][j][k][l]\),表示两个人在上面下面分别取了多少个,显然这样会炸掉
但是发现他们输出序列相同的必要条件是 \(i + j = k + l\),就可以把状态优化为
\(f[len][i][j]\),表示总共取 \(len\) 个,两个人分别在上下取 \(i,j\) 个得到相同输出序列的方案数
转移十分容易,需要使用滚动数组滚掉 \(len\) 降低空间开销
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
constexpr int maxn = 512,mod = 1024523;
int f[2][maxn][maxn],n,m;
char str1[maxn],str2[maxn];
int main(){
scanf("%d %d\n",&n,&m);
scanf("%s",str1 + 1);
scanf("%s",str2 + 1);
f[0][0][0] = 1;
for(int len = 0;len <= n + m;len++){
int now = len & 1;
memset(f[now ^ 1],0,sizeof(f[now ^ 1]));
for(int i = 0;i <= min(len,n);i++)
for(int j = 0;j <= min(len,n);j++){
if(f[now][i][j] >= mod)f[now][i][j] %= mod;
if(!f[now][i][j])continue;
if(str1[i + 1] == str1[j + 1])f[now ^ 1][i + 1][j + 1] += f[now][i][j];
if(str1[i + 1] == str2[len - j + 1])f[now ^ 1][i + 1][j] += f[now][i][j];
if(str2[len - i + 1] == str1[j + 1])f[now ^ 1][i][j + 1] += f[now][i][j];
if(str2[len - i + 1] == str2[len - j + 1])f[now ^ 1][i][j] += f[now][i][j];
}
}
printf("%d\n",f[(n + m) & 1][n][n]);
return 0;
}