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";
}