正题
题目链接:https://www.luogu.com.cn/problem/P1758
题目大意
给出一个大小为nnn和一个大小为mmm的栈,每次选择一个栈弹出栈顶然后记录这个字母,求所有弹出序列的弹出方案的二次方和。
1≤n,m≤5001\leq n,m\leq 5001≤n,m≤500
解题思路
二次方和可以看为取出方案相同的对数。
然后就是很简单的dpdpdp了,设fi,j,kf_{i,j,k}fi,j,k表示都取出了iii个,在第一个栈里分开取了j/kj/kj/k个,然后滚动。
时间复杂度O(nmn2)O(nmn^2)O(nmn2)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=510,P=1024523;
int n,m,f[N*2][N][N];
char s[N],t[N];
int main()
{scanf("%d%d",&n,&m);scanf("%s",s+1);scanf("%s",t+1);f[0][0][0]=1;for(int i=1;i<=n+m;i++)for(int j=0;j<=min(n,i);j++)for(int k=0;k<=min(n,i);k++){f[i&1][j][k]=0;if(s[j]==s[k]&&j&&k)(f[i&1][j][k]+=f[~i&1][j-1][k-1])%=P;if(s[j]==t[i-k]&&j&&i-k)(f[i&1][j][k]+=f[~i&1][j-1][k])%=P;if(t[i-j]==s[k]&&k&&i-j)(f[i&1][j][k]+=f[~i&1][j][k-1])%=P;if(t[i-j]==t[i-k]&&i-j&&i-k)(f[i&1][j][k]+=f[~i&1][j][k])%=P;}printf("%d\n",f[(n+m)&1][n][n]);return 0;
}