[BZOJ]1566: [NOI2009]管道取珠

Time Limit: 20 Sec  Memory Limit: 650 MB

Description

Input

  第一行包含两个整数n, m,分别表示上下两个管道中球的数目。 第二行为一个AB字符串,长度为n,表示上管道中从左到右球的类型。其中A表示浅色球,B表示深色球。 第三行为一个AB字符串,长度为m,表示下管道中的情形。

Output

  仅包含一行,即为 Sigma(Ai^2) i从1到k 除以1024523的余数。

Sample Input

  2 1
  AB
  B

Sample Output

  5

HINT

  样例即为文中(图3)。共有两种不同的输出序列形式,序列BAB有1种产生方式,而序列BBA有2种产生方式,因此答案为5。
  【大致数据规模】
  约30%的数据满足 n, m ≤ 12;
  约100%的数据满足n, m ≤ 500。

Solution

  相同方案的平方和等同于分别选出两个序列,它们相同的方案数(实在是妙……)。然后不难用f[i][j][k]表示两个序列各选了i个,第一个在第一行选j个,第二个在第一行选k个,两个序列相同的方案数,可以轻松DP求出答案。

Code

#include<cstdio>
#include<cstring>
#define MN 500
#define MOD 1024523
char a[MN+5],b[MN+5];
int f[2][MN+5][MN+5];
inline void rw(int&a,int b){a+=b;if(a>=MOD)a-=MOD;}
int main()
{
    int n,m,i,j,k;
    scanf("%d%d%s%s",&n,&m,a+1,b+1);
    f[0][0][0]=1;
    for(i=1;i<=n+m;++i)
    {
        memset(f[i&1],0,sizeof(f[i&1]));
        for(j=0;j<i&&j<=n;++j)for(k=0;k<i&&k<=n;++k)
        {
            if(j<n&&k<n&&a[j+1]==a[k+1])
                rw(f[i&1][j+1][k+1],f[~i&1][j][k]);
            if(j<n&&i-k<=m&&a[j+1]==b[i-k])
                rw(f[i&1][j+1][k],f[~i&1][j][k]);
            if(i-j<=m&&k<n&&b[i-j]==a[k+1])
                rw(f[i&1][j][k+1],f[~i&1][j][k]);
            if(i-j<=m&&i-k<=m&&b[i-j]==b[i-k])
                rw(f[i&1][j][k],f[~i&1][j][k]);
        }
    }
    printf("%d",f[(n+m)&1][n][n]);
}
原文地址:https://www.cnblogs.com/ditoly/p/BZOJ1566.html