【[NOI2009]管道取珠】

——\(shallwe\):这道题是\(noipDay2T2\)难度

好一个\(Day2T2\)难度啊,我觉得我可以退役了

平方和好像没有什么办法可以快速统计,于是考虑转化一下

我们可以将题意转化成这样

求有序对\((A,B)\),取法\(A\)可以和取法\(B\)得到相同的结果

也就是可以将题目抽象成一个人进行这个游戏两遍,能得到同样结果的方案数是多少

之后我们可以设计出这样的\(dp\)方程,\(dp[i][j][k][p]\)表示第一次取从上面那个管道里取出了\(i\)个,从下面那个管道里取出了\(j\)个,第二次从上面那个管道取出\(k\)个,从第二个管道里取出\(p\)个,得到的结果相同的方案数

结果相同肯定得取出的数量相同,所以\(i+j=k+p\),于是\(p\)那一维可以不要了

同时我们还可用滚掉一维,进一步优化空间

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define maxn 505
const int mod=1024523;
int n,m,o;
char A[maxn],B[maxn];
char a[maxn],b[maxn];
int dp[2][maxn][maxn];
inline int qm(int a,int b)
{
	int t=a+b;
	if(t>mod) return t-mod;
	return t;
}
int main()
{
	scanf("%d%d",&n,&m);
	scanf("%s",A+1);
	scanf("%s",B+1);
	for(re int i=1;i<=n;i++) a[i]=A[n-i+1];
	for(re int j=1;j<=m;j++) b[j]=B[m-j+1];
	dp[0][0][0]=1;
	for(re int i=0;i<=n;i++,o^=1)
		for(re int j=0;j<=m;j++)
			for(re int k=0;k<=n;k++)
			{
				int p=i+j-k;
				if(p<0||p>m) continue;
				if(a[i+1]==a[k+1]) dp[o^1][j][k+1]=qm(dp[o^1][j][k+1],dp[o][j][k]);
				if(b[j+1]==b[p+1]) dp[o][j+1][k]=qm(dp[o][j+1][k],dp[o][j][k]);
				if(a[i+1]==b[p+1]) dp[o^1][j][k]=qm(dp[o^1][j][k],dp[o][j][k]);
				if(b[j+1]==a[k+1]) dp[o][j+1][k+1]=qm(dp[o][j+1][k+1],dp[o][j][k]);
				dp[o][j][k]=0;
			}
	std::cout<<dp[o][m][n];
	return 0;
}
posted @ 2019-01-01 19:51  asuldb  阅读(135)  评论(0编辑  收藏  举报