[NOI2009]管道取珠
直接统计 \(\sum {a_i} ^ 2\) 非常不好统计,可以考虑转化题意。
于是有了下面一个想法,我们只需要让每种方案重复记录 \(a_i\) 次即可,因为我们无法每个方案的 \(a_i\),因此我们只能退而求其次在对每种方案计数时搞个什么东西让每种方案恰好算 \(a_i\) 次,不难发现我们可以看作是两个人同时在玩这个游戏取到同种取珠方案的方案数。那么我们就可以直接令 \(dp_{i, j, k}\) 为当前取到第 \(i\) 轮,第一个人上管道还剩 \(j\) 个珠,第二个人上管道还剩 \(k\) 个珠两个人取到相同方案的方案数,转移就非常显然了。
#include<bits/stdc++.h>
using namespace std;
#define N 500 + 5
#define Mod 1024523
#define rep(i, l, r) for(int i = l; i <= r; ++i)
char a[N], b[N];
int n, m, t, dp[2][N][N];
int read(){
char c; int x = 0, f = 1;
c = getchar();
while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int Inc(int a, int b){
return (a += b) >= Mod ? a - Mod : a;
}
int main(){
n = read(), m = read();
scanf("%s%s", a + 1, b + 1);
reverse(a + 1, a + n + 1), reverse(b + 1, b + m + 1);
dp[t][0][0] = 1, t = 1;
rep(i, 1, n + m){
int s = max(0, i - m), e = min(n, i);
rep(j, s, e) rep(k, s, e){
if(j && k && a[j] == a[k]) dp[t][j][k] = Inc(dp[t][j][k], dp[t ^ 1][j - 1][k - 1]);
if(j && i - k >= 1 && a[j] == b[i - k]) dp[t][j][k] = Inc(dp[t][j][k], dp[t ^ 1][j - 1][k]);
if(k && i - j >= 1 && b[i - j] == a[k]) dp[t][j][k] = Inc(dp[t][j][k], dp[t ^ 1][j][k - 1]);
if(i - k >= 1 && i - j >= 1 && b[i - j] == b[i - k]) dp[t][j][k] = Inc(dp[t][j][k], dp[t ^ 1][j][k]);
}
s = max(0, i - m - 1), e = min(n, i - 1), t ^= 1;
rep(j, s, e) rep(k, s, e) dp[t][j][k] = 0;
}
printf("%d", dp[t ^ 1][n][n]);
return 0;
}