[NOI2009]管道取珠

[NOI2009]管道取珠

给出一个长度为n的01序列\(\{a_i\}\)和一个长度为m的01序列\(\{b_i\}\),给出\(n+m\)个格子,按顺序将a和b填入,也就是事先选好n个位置,按顺序地填入a,然后其它的按顺序填入b,设填后的格子有\(c\)次重复,问所有的格子填法\(c^2\)的和,\(n,m\leq 500\)

平方可以转换为两个人填,方案相同的方案数,于是设\(f[i][j][k]\)表示两个人都填到了第i个个子,两人分别填到a序列中的第j个位置第k个位置的方案相同的方案数。

显然可以得知它们分别填到b序列的第\(i-j,i-k\)个位置,于是有

\[f[i][j][k]=\begin{cases}f[i-1][j-1][k-1](a[j]==a[k])\\f[i-1][j-1][k](a[j]==b[i-k])\\f[i-1][j][k-1](b[i-j]==a[k])\\f[i-1][j][k](b[i-j]==b[i-k])\end{cases}\]

显然边界为\(f[0][0][0]=1\),答案是\(f[n+m][n][m]\)

参考代码:

#include <iostream>
#include <cstdio>
#define il inline
#define ri register
#define Size 550
#define gzy 1024523
using namespace std;
bool a[Size],b[Size];
int dp[Size][Size];
il void get(char&);
int main(){
    char c;int n,m;scanf("%d%d",&n,&m);
    for(ri int i(1);i<=n;++i)get(c),a[i]=c=='A';
    for(ri int i(1);i<=m;++i)get(c),b[i]=c=='A';
    dp[0][0]=1;
    for(ri int i(1),j,k;i<=n+m;++i)
        for(j=min(i,n);j>=0;--j)
            for(k=min(i,n);k>=0;--k){
                if(!(i-j)||!(i-k)||b[i-j]!=b[i-k])dp[j][k]=0;
                if(k&&j&&a[j]==a[k])dp[j][k]+=dp[j-1][k-1];
                if(j&&a[j]==b[i-k]&&i-k)dp[j][k]+=dp[j-1][k];
                if(k&&b[i-j]==a[k]&&i-j)dp[j][k]+=dp[j][k-1];
                dp[j][k]%=gzy;
            }printf("%d",dp[n][n]);
    return 0;
}
il void get(char &c){
    while(c=getchar(),c==' '||c=='\n'||c=='\r');
}

转载于:https://www.cnblogs.com/a1b3c7d9/p/11429119.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值