[BZOJ1566][NOI2009]管道取珠
[BZOJ1566][NOI2009]管道取珠
试题描述
输入
第一行包含两个整数n, m,分别表示上下两个管道中球的数目。 第二行为一个AB字符串,长度为n,表示上管道中从左到右球的类型。其中A表示浅色球,B表示深色球。 第三行为一个AB字符串,长度为m,表示下管道中的情形。
输出
仅包含一行,即为 Sigma(Ai^2) i从1到k 除以1024523的余数。
输入示例
2 1 AB B
输出示例
5
数据规模及约定
约30%的数据满足 n, m ≤ 12;
约100%的数据满足n, m ≤ 500。
题解
这题思路比较妙,我们需要先想想 ∑ai2 有什么意义。如果我们构造出这样一个游戏场景,即两个人同时玩两份同样的如题目所述的管道取珠的游戏,那么这两个人游戏结束后取到的珠子颜色序列一模一样的方案数就是题目里要求的答案。
令这两个人分别是 p1 和 p2。于是设 f[i][j][k] 表示 p1 取了第二个管道中的前 i 个珠子,第一个管道中的前 j 个珠子;p2 取了第一个管道的前 k 个珠子,这个状态下颜色序列相同的方案数,转移显然。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 510 #define MOD 1024523 int n, m, f[2][maxn][maxn]; bool has[2][maxn][maxn]; char A[maxn], B[maxn]; int main() { n = read(); m = read(); scanf("%s%s", A + 1, B + 1); int cur = 0; f[0][0][0] = 1; has[0][0][0] = 1; for(int i = 0; i <= m; i++, cur ^= 1) { for(int j = 0; j <= n; j++) for(int k = 0; k <= n; k++) f[cur^1][j][k] = has[cur^1][j][k] = 0; for(int j = 0; j <= n; j++) for(int k = 0; k <= n; k++) if(has[cur][j][k]) { // printf("%d %d %d: %d\n", i, j, k, f[cur][j][k]); int bj = i, bk = i + j - k; if(j < n && k < n && A[j+1] == A[k+1]) { f[cur][j+1][k+1] += f[cur][j][k]; has[cur][j+1][k+1] = 1; if(f[cur][j+1][k+1] >= MOD) f[cur][j+1][k+1] -= MOD; } if(j < n && bk < m && A[j+1] == B[bk+1]) { f[cur][j+1][k] += f[cur][j][k]; has[cur][j+1][k] = 1; if(f[cur][j+1][k] >= MOD) f[cur][j+1][k] -= MOD; } if(bj < m && k < n && B[bj+1] == A[k+1]) { f[cur^1][j][k+1] += f[cur][j][k]; has[cur^1][j][k+1] = 1; if(f[cur^1][j][k+1] >= MOD) f[cur^1][j][k+1] -= MOD; } if(bj < m && bk < m && B[bj+1] == B[bk+1]) { f[cur^1][j][k] += f[cur][j][k]; has[cur^1][j][k] = 1; if(f[cur^1][j][k] >= MOD) f[cur^1][j][k] -= MOD; } } } printf("%d\n", f[cur^1][n][n]); return 0; }