CF570E 题解

CF570E

这题是 P3126 加强版。

思路

要走成回文的路径,即从左上角 \((1,1)\) 开始向右下和右下角 \((n,n)\) 开始向左上,走至相字母字母都相同。dp 计数。

\(dp_{i,j,jj,k,kk}\) 表示走了 \(i\) 步后,从左上走到 \((j,jj)\) ,从右下走至 \((k,kk)\) 的方案数。

转移如下:

\(dp_{i,j,jj,k,kk}=dp_{i-1,j-1,jj,k+1,kk}+dp_{i-1,j-1,jj,k,kk+1}+dp_{i-1,j,jj-1,k+1,kk}+dp_{i-1,j,jj-1,k,kk+1}\)

因为只能向右下或左上走,所以当走了 \(i\) 步后两边所在的位置是可以用 \(i\) 和横坐标表示的。可以分别表示为 \((j,2+i-1-j)\)\((k,n+m-i+1-k)\)

所以状态改为设 \(dp_{i,j,k}\) 表示走了 \(i\) 步后,从左上走到 \((j,2+i-1-j)\),从右下走至 \((k,n+m-i+1-k)\) 的方案数。

\[dp_{i,j,k}=dp_{i-1,j-1,k+1}+dp_{i-1,j-1,k}+dp_{i-1,j,k+1}+dp_{i-1,j,k} \]

最后可能相遇或相邻,所以,当 \(n+m\) 为偶时相遇,答案来自 \(dp_{(n+m)/2,i,i}\),否则相邻,答案来自 \(dp_{(n+m)/2,i,i}+dp_{(n+m)/2,i,i+1}\)

code

int n,m,t,dp[3][maxn][maxn],ans;
char c[maxn][maxn];
void sovle(){
	cin>>n>>m;t=n+m>>1;
	for(int i=1;i<=n;i++)cin>>c[i]+1;
	if(c[1][1]!=c[n][m]){
		cout<<"0\n";
		return ;
	}
	dp[1][1][n]=1;
	for(int i=2;i<=t;i++){
		for(int j=1;j<=min(i,n);j++){
			for(int k=n;k>=max(n-i,j);k--){
				int jj,kk;dp[i&1][j][k]=0;
				jj=2+i-1-j;kk=n+m-i+1-k;
//				cout<<i<<" "<<j<<" "<<jj<<" "<<k<<" "<<kk<<"\n";
				if(c[j][jj]==c[k][kk]){
					dp[i&1][j][k]+=dp[(i-1)&1][j-1][k+1],dp[i&1][j][k]%=mod;
					dp[i&1][j][k]+=dp[(i-1)&1][j-1][k],dp[i&1][j][k]%=mod;
					dp[i&1][j][k]+=dp[(i-1)&1][j][k+1],dp[i&1][j][k]%=mod;
					dp[i&1][j][k]+=dp[(i-1)&1][j][k],dp[i&1][j][k]%=mod;
				}
//				cout<<dp[i&1][j][k]<<"\n";
			}
		}
	}
	if((n+m)&1)for(int i=1;i<=n;i++)ans+=dp[t&1][i][i+1],ans%=mod,ans+=dp[t&1][i][i],ans%=mod;
	else for(int i=1;i<=n;i++)ans+=dp[t&1][i][i],ans%=mod;
	cout<<ans<<"\n";
}
posted @ 2024-05-10 19:48  yhddd  阅读(30)  评论(0)    收藏  举报