[BZOJ1566][NOI2009]管道取珠(dp)

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=1566

Solution

下面增加一个定义:
f [ i ] [ j ] 生成的颜色序列」表示上管道前 i 个珠子和下管道前 j 个珠子移到输出管道生成的颜色序列。
想了一波,写完后发现和标解不一样
首先容易定义出一个状态:
f [ i ] [ j ] 表示上管道前 i 个珠子和下管道 j 个珠子组成的不同序列的方案数平方和。
边界显然 f [ 0 ] [ 0 ] = 1
如果上管道的第 i 个珠子与下管道的第 j 个珠子异色,那么对于任一上管道前 i 1 个珠子和下管道前 j 个珠子生成的颜色序列 X 和 上管道 i 个珠子及下管道前 j 1 个珠子生成的颜色序列 Y ,都有 X [ i + j ] Y [ i + j ] X 不可能与 Y 相同。
这时转移:

f [ i ] [ j ] = f [ i 1 ] [ j ] + f [ i ] [ j 1 ]

否则如果 X = Y = R f [ i 1 ] [ j ] 生成 R 的方案数为 x f [ i ] [ j 1 ] 生成 R 的方案数为 y ,那么 f [ i ] [ j ] 生成 R 的方案数应为 ( x + y ) 2 = x 2 + 2 x y + y 2 而不是 x 2 + y 2
所以还要定义状态 g [ i ] [ j ] [ k ] ,表示:
上管道的前 i 个珠子和下管道的前 j 个珠子生成的序列 S ,以及上管道的前 k 个珠子和下管道的前 h = i + j k 个珠子生成的序列 T ,对于所有 S = T f [ i ] [ j ] 生成 S 的方案数乘以 f [ k ] [ h ] 生成 T 的方案数之和。
g [ 0 ] [ 0 ] [ 0 ] = 1

先从 g [ i 1 ] [ j ] [ k 1 ] 转移讨论:
如果上管道第 i 个珠子和第 k 个珠子不同色,那么 f [ i ] [ j ] f [ k ] [ h ] 不可能用这种方式生成相同的颜色序列。
根据
( x + y ) ( a + b ) = a x + b x + a y + b y

我们可以把状态定义中的乘积进行拆分,拆分成 4 个乘积的和。
也就是说,设 q [ i ] [ j ] [ S ] 表示 f [ i ] [ j ] 生成 S 的方案数, 那么显然有:
q [ i ] [ j ] [ S ] = q [ i 1 ] [ j ] [ S [ i ] ] ( [ i ] = S [ i + j ] )
+ q [ i ] [ j 1 ] [ S [ i ] ] ( [ j ] = S [ i + j ] )

于是转移也就得出了:
g [ i ] [ j ] [ k ] + = g [ i 1 ] [ j ] [ k 1 ]

(如果上管道第 i 个珠子和第 k 个珠子同色)
g [ i ] [ j ] [ k ] + = g [ i ] [ j 1 ] [ k 1 ]

(如果下管道第 j 个珠子和上管道第 k 个珠子同色)
g [ i ] [ j ] [ k ] + = g [ i 1 ] [ j ] [ k ]

(如果上管道第 i 个珠子和下管道第 h 个珠子同色)
g [ i ] [ j ] [ k ] + = g [ i ] [ j 1 ] [ k ]

(如果下管道第 j 个珠子和第 h 个珠子同色)
回到 [ i ] = [ j ] f [ i ] [ j ] 的转移:
f [ i ] [ j ] = f [ i 1 ] [ j ] + f [ i ] [ j 1 ] + g [ i 1 ] [ j ] [ i ] × 2

最后结果:
f [ n ] [ m ]

注意滚动。

Code

// luogu-judger-enable-o2
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
const int N = 505, ZZQ = 1024523;
int n, m, g[2][N][N], f[N][N];
char s[N], t[N];
int main() {
    int i, j, k;
    cin >> n >> m;
    scanf("%s%s", s + 1, t + 1);
    For (i, 0, n) For (j, 0, m) {
        int o = i & 1;
        For (k, 0, min(n, i + j)) {
            if (!i && !j && !k) {g[o][j][k] = 1; continue;}
            g[o][j][k] = 0; int h = i + j - k;
            if (i && k && s[i] == s[k]) g[o][j][k] += g[o ^ 1][j][k - 1];
            if (j && k && t[j] == s[k]) g[o][j][k] += g[o][j - 1][k - 1];
            if (i && h && s[i] == t[h]) g[o][j][k] += g[o ^  1][j][k];
            if (j && h && t[j] == t[h]) g[o][j][k] += g[o][j - 1][k];
            g[o][j][k] %= ZZQ;
        }
        if (!i && !j) f[i][j] = 1;
        else {
            f[i][j] = 0;
            if (i) f[i][j] += f[i - 1][j];
            if (j) f[i][j] += f[i][j - 1];
            if (s[i] == t[j]) f[i][j] += g[o ^ 1][j][i] << 1;
            f[i][j] %= ZZQ;
        }
    }
    cout << f[n][m] << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/81283011