题目链接

题目大意:有黑球和白球排成两行,每次可以从任意一行排头取一个球放到输出序列尾部。设得到第 \(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;
}

相关文章: