[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
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]); }