1. 程式人生 > >CF570E Pig and Palindromes

CF570E Pig and Palindromes

pen img bsp () set 表示 esp ace ++

完全不會這種類型的$dp$啊……

考慮回文串一定是可以拆分成(偶數個字母 + 偶數個字母)或者(偶數個字母 + 一個字母 +偶數個字母),兩邊的偶數個字母其實是完全對稱的。因為這道題回文串的長度是給定的$n + m$,所以回文串的類型也是確定的。

發現直接$dp$不好轉移,我們可以把走的步數拆成兩半,從$(1, 1)$開始走$(n + m) / 2$步,從$(n, m)$開始走$(n + m) / 2$步,然後在中間相遇就可以計算答案,這樣子只要每一次走到相同的格子就可以轉移了。

我們先設計出一個暴力的狀態就是$f_{stp, xa, ya, xb, yb}$表示走了$stp$步,從$(1, 1)$開始走到$(xa, ya)$,從$(n, m)$開始走到$(xb, yb)$的方案數。

顯然空間炸了。

觀察一下發現了$stp$這一維可以滾掉,而當$stp$確定時,只要知道了$xa$和$xb$就可以計算出$ya$和$yb$,具體計算過程可以自己$yy$一下。

最後統計答案的時候要註意討論$(n + m)$的奇偶性。

時間復雜度$O(n^3)$。

Code:

技術分享圖片
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N = 505;
const ll P = 1e9 + 7
; int n, m; ll f[2][N][N]; char mp[N][N]; template <typename T> inline void inc(T &x, T y) { x += y; if(x >= P) x -= P; } int main() { scanf("%d%d", &n, &m); /* scanf("%d", &n); m = n; */ for(int i = 1; i <= n; i++) scanf("%s", mp[i] + 1
); if(mp[1][1] != mp[n][m]) return puts("0"), 0; f[1][1][n] = 1LL; for(int s = 2; s <= (n + m) / 2; ++s) { int now = s & 1, pre = (s - 1) & 1; memset(f[now], 0LL, sizeof(f[now])); for(int xa = 1; xa <= s; ++xa) for(int xb = n; xb >= n - s + 1; --xb) { int ya = s + 1 - xa, yb = n + m + 1 - s - xb; if(xa > xb || ya > yb) continue; if(mp[xa][ya] != mp[xb][yb]) continue; inc(f[now][xa][xb], f[pre][xa][xb]); inc(f[now][xa][xb], f[pre][xa - 1][xb]); inc(f[now][xa][xb], f[pre][xa][xb + 1]); inc(f[now][xa][xb], f[pre][xa - 1][xb + 1]); } } ll ans = 0LL; int cur = ((n + m) / 2) & 1; if((n + m) % 2 == 1) { for(int i = 1; i <= n; i++) { inc(ans, f[cur][i][i]); inc(ans, f[cur][i][i + 1]); } } else { for(int i = 1; i <= n; i++) inc(ans, f[cur][i][i]); } printf("%lld\n", ans); return 0; }
View Code

CF570E Pig and Palindromes