zoukankan      html  css  js  c++  java
  • CF570E Pig and Palindromes

    完全不会这种类型的$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
    ", ans);
        return 0;
    }
    View Code
  • 相关阅读:
    树剖学习笔记
    网络命令
    shell编程
    Shell 脚本常用命令
    WP7系统托盘和应用程序栏
    Ajax的同步与异步
    WP7推送通知服务
    (转)Silverlight控件关系理解
    (转) silverlight 样式学习
    (转C#中Enum用法小结)
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/9802931.html
Copyright © 2011-2022 走看看