题解 P1758 【[NOI2009]管道取珠】

题目链接

Solution [NOI2009]管道取珠

题目大意:有黑球和白球排成两行,每次可以从任意一行排头取一个球放到输出序列尾部。设得到第 (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,&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
",f[(n + m) & 1][n][n]);
    return 0;
}
原文地址:https://www.cnblogs.com/colazcy/p/13930108.html