题解 回文
这题不会做,太丢人了
考场上试图合并路径什么的,毫无进展
想DP但完全不知道怎么定义
但正解其实就是DP
- 关于图上回文路径的一类处理方法:
考虑从起点和终点同时开始DP,回文的性质可以用长度+转移保证
具体的,令 \(f_{i, j, k}\) 为走了 \(i\) 步,其中从起点向下走了 \(j\) 步,从终点向上走了 \(k\) 步的方案数
然后转移考虑下一个落脚点的权值是否相等,若相等再转移
于是可以BFS实现,复杂度 \(O(n^3)\)
但是这样就可以通过这题了吗?
注意到 \(n=500\),在某TLEcoders上显然是过不去的
然后有一个写法:
BFS中产生了一些很难剪掉的冗余状态,且常数较大
这整个过程都可以用循环一遍DP完成,常数极小
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 502
#define ll long long
#define ull unsigned long long
#define fir first
#define sec second
#define make make_pair
//#define int long long
int n, m;
char mp[N][N];
const int mod=993244853;
inline void md(int& a, int b) {a+=b; a=a>=mod?a-mod:a;}
namespace force{
int lim;
ll ans;
const ull base=13131;
// unordered_map<ull, ll> mp1, mp2;
int tot1, tot2;
pair<ull, pair<int, int>> mp1[1000100], mp2[1000100];
void dfs1(int u, int v, int len, ull pre) {
// cout<<"dfs1: "<<u<<' '<<v<<endl;
pre=pre*base+mp[u][v];
if (len+1>lim) mp1[++tot1]=make(pre, make(u, v));
else {
if (u+1<=n) dfs1(u+1, v, len+1, pre);
if (v+1<=m) dfs1(u, v+1, len+1, pre);
}
}
void dfs2(int u, int v, int len, ull pre) {
pre=pre*base+mp[u][v];
if (len+1>lim) mp2[++tot2]=make(pre, make(u, v));
else {
if (u-1>=1) dfs2(u-1, v, len+1, pre);
if (v-1>=1) dfs2(u, v-1, len+1, pre);
}
}
bool check(pair<int, int> a, pair<int, int> b) {
if (a.fir+1==b.fir && a.sec==b.sec) return 1;
else if (a.fir==b.fir && a.sec+1==b.sec) return 1;
else return 0;
}
void solve() {
lim=(n+m-1)/2;
dfs1(1, 1, 1, 0);
dfs2(n, m, 1, 0);
sort(mp1+1, mp1+tot1+1); sort(mp2+1, mp2+tot2+1);
for (int i=1; i<=tot1; ++i) {
for (int j=1; j<=tot2; ++j) if (mp1[i].fir==mp2[j].fir)
if (check(mp1[i].sec, mp2[j].sec)) ++ans;
}
printf("%lld\n", ans);
exit(0);
}
}
namespace task1{
int lim;
ll ans;
const ull base=13131;
// unordered_map<ull, ll> mp1, mp2;
int tot1, tot2;
vector<char> tem, t2;
void check() {
t2=tem;
reverse(t2.begin(), t2.end());
if (t2==tem) ++ans;
}
void dfs1(int u, int v) {
// cout<<"dfs1: "<<u<<' '<<v<<endl;
tem.push_back(mp[u][v]);
if (u==n && v==m) {
check();
tem.pop_back();
return ;
}
if (u+1<=n) dfs1(u+1, v);
if (v+1<=m) dfs1(u, v+1);
tem.pop_back();
}
void solve() {
dfs1(1, 1);
printf("%lld\n", ans);
exit(0);
}
}
namespace task2{
int dp[N][N][N], ans;
bool vis[N][N][N];
struct tpl{
short fir, sec, thr;
tpl(){}
tpl(short x, short y, short z):fir(x),sec(y),thr(z){}
inline void build(const short& x, const short& y, const short& z){fir=x; sec=y; thr=z;}
}q[500*500*500+1];
int ql, qr;
void solve() {
short lim=(n+m-2)/2+2;
// cout<<"lim: "<<lim<<endl;
dp[1][1][n]=1; vis[1][1][n]=1;
// q.push(tpl(1, 1, n));
q[++qr]=tpl(1, 1, n);
tpl u;
short a, b, c, d;
while (ql<=qr) {
u=q[ql++];
// cout<<"u: "<<u.fir<<' '<<u.sec<<' '<<u.thr<<endl;
if (u.fir+u.sec>=lim) continue;
a=u.fir, b=u.sec, c=u.thr, d=n+m-u.fir-u.sec+2-u.thr;
// cout<<a<<' '<<b<<' '<<c<<' '<<d<<endl;
if (a>c || b>d) continue;
if (abs(a-c)+abs(b-d)>2*(lim-u.fir-u.sec)+1) continue;
// cout<<"cd: "<<c<<' '<<d<<endl;
if (a+1<=n) {
if (mp[a+1][b]==mp[c-1][d]) {
md(dp[a+1][b][c-1], dp[u.fir][u.sec][u.thr]);
if (!vis[a+1][b][c-1]) q[++qr].build(a+1, b, c-1), vis[a+1][b][c-1]=1;
}
if (mp[a+1][b]==mp[c][d-1]) {
md(dp[a+1][b][c], dp[u.fir][u.sec][u.thr]);
if (!vis[a+1][b][c]) q[++qr].build(a+1, b, c), vis[a+1][b][c]=1;
}
}
if (b+1<=m) {
if (mp[a][b+1]==mp[c-1][d]) {
md(dp[a][b+1][c-1], dp[u.fir][u.sec][u.thr]);
if (!vis[a][b+1][c-1]) q[++qr].build(a, b+1, c-1), vis[a][b+1][c-1]=1;
}
if (mp[a][b+1]==mp[c][d-1]) {
md(dp[a][b+1][c], dp[u.fir][u.sec][u.thr]);
if (!vis[a][b+1][c]) q[++qr].build(a, b+1, c), vis[a][b+1][c]=1;
}
}
}
if ((n+m)&1) {
for (int a=1; a<=n; ++a) {
int b=lim-a, c, d;
c=a+1, d=b;
if (c<=n) md(ans, dp[a][b][c]);
c=a, d=b+1;
if (d<=m) md(ans, dp[a][b][c]);
}
}
else {
for (int a=1; a<=n; ++a) {
int b=lim-a, c, d;
c=a, d=n+m-a-b+2-c;
// cout<<"test: "<<a<<' '<<b<<' '<<c<<' '<<d<<endl;
if (a==c && b==d) md(ans, dp[a][b][c]);
}
}
printf("%d\n", ans);
exit(0);
}
}
namespace task{
int dp[N][N][N], ans;
void solve() {
int lim=(n+m-2)/2+2;
if (mp[1][2]==mp[n][m-1]) dp[1][0][0]=1;
if (mp[1][2]==mp[n-1][m]) dp[1][0][1]=1;
if (mp[2][1]==mp[n][m-1]) dp[1][1][0]=1;
if (mp[2][1]==mp[n-1][m]) dp[1][1][1]=1;
for (int i=1; i<=(n+m-2)>>1; ++i) {
for (int j=max(0, i-m+1); j<=min(i, n-1); ++j) {
for (int k=max(0, i-m+1); k<=min(i, n-1); ++k) {
if (mp[1+j][1+i-j]!=mp[n-k][m-i+k]) continue;
// cout<<j<<' '<<k<<endl;
if (j) {
if (k) md(dp[i][j][k], dp[i-1][j-1][k-1]);
if (i-k) md(dp[i][j][k], dp[i-1][j-1][k]);
}
if (i-j) {
if (k) md(dp[i][j][k], dp[i-1][j][k-1]);
if (i-k) md(dp[i][j][k], dp[i-1][j][k]);
}
// printf("dp[%d][%d][%d]=%d\n", i, j, k, dp[i][j][k]);
}
}
}
if ((n+m)&1) for (int i=0; i<n-1; ++i) md(ans, dp[(n+m-2)/2][i][n-i-2]); //, cout<<i<<' '<<n-i-2<<' '<<dp[(n+m-2)/2][i][n-i-2]<<endl;
// cout<<"---"<<endl;
for (int i=0; i<n; ++i) md(ans, dp[(n+m-2)/2][i][n-i-1]); //, cout<<i<<' '<<n-i-1<<' '<<dp[(n+m-2)/2][i][n-i-1]<<endl;
printf("%d\n", ans);
exit(0);
}
}
signed main()
{
freopen("palin.in", "r", stdin);
freopen("palin.out", "w", stdout);
scanf("%d%d", &n, &m);
// if ((n+m)%2==0) {puts("0"); return 0;}
for (int i=1; i<=n; ++i) scanf("%s", mp[i]+1);
if (mp[1][1]!=mp[n][m]) {puts("0"); return 0;}
// force::solve();
task::solve();
return 0;
}