第一次X人,连X7发,然后就遭报应了最后跌跌撞撞写完C没交上去T_T

E

很不错的一道dp,求从矩形的(1,1)到(m,n)的回文串数量
我们联想一维的回文子串数量是怎么求的$d_{i,j}$表示i-j的回文子串数量
$$d_{i,j} = d_{i+1,j} + d_{i,j-1} + d_{i+1,j-1} \;,\; (if s_{i-1}=s_{j-1} \,,\, i<j-1)$$
我们令$d_{x_1,y_1,x_2,y_2}$ 表示从(x1,y1)到(x2,y2)的回文串数量,我们很容易写出记忆化搜索
我们发现x1+y1 = m+n-x2+y2,记忆化只需要记录x1,y1,x2,而y2可以由这三项确定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int dp(int x1,int y1,int x2,int y2)
{
if(x1>x2 || y1>y2) return 0;
if(pic[x1][y1] != pic[x2][y2]) return 0;
if(x1 == x2 || y1 == y2)
{
if(x1==x2)
{
if(y2 - y1<=2) return pic[x1][y1] == pic[x2][y2];
}
else
{
if(x2 - x1<=2) return pic[x1][y1] == pic[x2][y2];
}
}
int &ans = d[x1][y1][x2];
if(ans!=-1) return ans;
ans = 0;
ans = (ans+dp(x1+1,y1,x2-1,y2)) % mod;
ans = (ans+dp(x1,y1+1,x2-1,y2)) % mod;
ans = (ans+dp(x1+1,y1,x2,y2-1)) % mod;
ans = (ans+dp(x1,y1+1,x2,y2-1)) % mod;
return ans;
}

然而这样做会MLE,我们需要用时间换空间了。
我们定义$d_{i,x_1,x_2}$表示从横坐标x1到x2的方案数,其中i=x1+y1,为斜向划分的若干阶段,每一阶段都可以由i得出y1和y2,这样就可以用滚动数组存储了。
$$d_{i,x_1+1,x_2-1} += d_{i-1,x_1+1,x_2} + d_{i-1,x_1,x_2-1} + d_{i-1,x_1+1,x_2-1} + d_{i-1,x_1,x_2} $$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include<bits/stdc++.h>
using namespace std;

const int mod = 1e9 + 7;
const int maxn = 510;
int d[2][maxn][maxn];
char pic[maxn][maxn];

inline void add(int &a,int b)
{
a += b;
if(a>=mod) a-=mod;
}

int main()
{
//freopen("test.txt","r",stdin);
int n,m;
while(scanf("%d %d",&n,&m)==2)
{
for(int i=0;i<n;++i) scanf("%s",pic[i]);
if(pic[0][0]!=pic[n-1][m-1])
{
puts("0");
continue;
}
memset(d,0,sizeof d);
int cur = 0;
d[cur][0][n-1] = 1;
int N = (m+n-2);
for(int t=1;t<=N/2;++t)
{
cur^=1;
memset(d[cur],0,sizeof d[cur]);
for(int i=0;i<=t;++i)
{
for(int k=n-t-1;k<n;++k)
{
// printf("(%d,%d) (%d,%d)\n",i,t-i,k,N-t-k);
if(i>k || t-i > N-t-k) continue;
if(pic[i][t-i]==pic[k][N-t-k])
{
add(d[cur][i][k],d[1-cur][i][k]);
if(i-1>=0) add(d[cur][i][k],d[1-cur][i-1][k]);
if(k+1<n) add(d[cur][i][k],d[1-cur][i][k+1]);
if(i-1>=0 && k+1<n) add(d[cur][i][k],d[1-cur][i-1][k+1]);
}
}
}
}
int ans = 0 , t=N/2;
if(N&1)
{
for(int i=0;i<=t;++i)
{
add(ans,d[cur][i][i+1]);
add(ans,d[cur][i][i]);
}
}
else
{
for(int i=0;i<=t;++i)
{
if(i-1>=0) add(ans,d[cur][i-1][i]);
add(ans,d[cur][i][i]);
if(i+1<n) add(ans,d[cur][i][i+1]);
if(i-1>=0 && i+1<n) add(ans,d[cur][i+1][i-1]);
}
}
printf("%d\n",ans);
}
return 0;
}