NOI2009 第二场 B 题解

洛谷 P1758 管道取珠(ball)

题目大意

两个栈,自底向上分别有 \(n,m\) 个球,每个球都可能是 AB 两种颜色之一。取 \(n+m\) 次球,每次取球从两个栈的栈顶弹出一个,先后排列,记录下颜色,则会产生长 \(n+m\) 的颜色序列。设一共可能产生 \(k\) 种不同的颜色序列,每种序列有 \(a_i\) 种取法,显然

\[\sum_{i=1}^ka_i=\binom{n+m}m, \]

但是我们要求

\[\sum_{i=1}^ka_i^2 \]

\(1~024~573\) 取模的结果。

数据范围

  • \(1\le n,m\le 500\)

思路

既然 \(a_i\) 是取一次的结果,那么 \(a_i^2\) 可不可以看成取两次呢?我们这样想:把两个栈复制一份,让两个人分别去取,每个人有 \(a_i\) 种取法,那么合到一起就是 \(a_i^2\) 种情况了。考虑 DP:设 \(f_{i,j,k}\) 为,两个人都拿了 \(i\) 个球,其中,第一个人从第一个栈拿了 \(j\) 个球,第二个人从第一个栈拿了 \(k\) 个球,此时两个人拿球的序列相同的方案数。

那么,显然只有栈顶颜色一样的情况才能转移。我们令 \(A_i,B_i\) 分别为两个栈自顶向下\(i\) 个球的颜色,开始转移:

\[\begin{aligned} f_{i,j,k}&=f_{i-1,j-1,k-1}\times[j\ge1\wedge k\ge1\wedge A_j=B_k]\\ &+f_{i-1,j,k-1}\times[i-j\ge1\wedge k\ge1\wedge B_{i-j}=A_k]\\ &+f_{i-1,j-1,k}\times[j\ge1\wedge i-k\ge1\wedge A_j=B_{i-k}]\\ &+f_{i-1,j,k}\times[i-j\ge1\wedge i-k\ge1\wedge B_{i-j}=B_{i-k}], \end{aligned} \]

其中中括号为艾弗森括号,\(\wedge\) 相当于 C++ 中的 &&and。如果看晕了,就看最后一个 \(\wedge\) 后面那坨,前面都是判边界的。初始状态是

\[f_{0,0,0}=1, \]

最终答案为

\[f_{n+m,n,n}. \]

时间复杂度 \(O(n^3+mn^2)\approx O(n^3)\)

AC 代码

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int mod = 1'024'523;
char a[505],b[505];
long long ZRX[2][505][505];

#define f(i,j,k) ZRX[(i)&1][j][k]

int main() {
    int n,m; scanf("%d%d",&n,&m);
    scanf("%s%s",a+1,b+1);
    reverse(a+1,a+n+1);
    reverse(b+1,b+m+1);
    f(0,0,0) = 1;
    for (int i = 1; i <= n+m; i++) {
        for (int j = 0; j <= n; j++)
            for (int k = 0; k <= n; k++)
                f(i,j,k) = 0;
        for (int j = 0; j <= n; j++)
            for (int k = 0; k <= n; k++) {
                if (j >= 1 && k >= 1 && a[j] == a[k])
                    f(i,j,k) = (f(i,j,k)+f(i-1,j-1,k-1))%mod;
                if (i-j >= 1 && k >= 1 && b[i-j] == a[k])
                    f(i,j,k) = (f(i,j,k)+f(i-1,j,k-1))%mod;
                if (j >= 1 && i-k >= 1 && a[i] == b[i-k])
                    f(i,j,k) = (f(i,j,k)+f(i-1,j-1,k))%mod;
                if (i-j >= 1 && i-k >= 1 && b[i-j] == b[i-k])
                    f(i,j,k) = (f(i,j,k)+f(i-1,j,k))%mod;
            }
    }
    printf("%lld",f(n+m,n,n));
    return 0;
}
posted @ 2026-06-06 17:01  Sternenlicht12345  阅读(9)  评论(0)    收藏  举报