CF 316div2 E.Pig and Palindromes

E. Pig and Palindromes

Peppa the Pig was walking and walked into the forest. What a strange coincidence! The forest has the shape of a rectangle, consisting of n rows and m columns. We enumerate the rows of the rectangle from top to bottom with numbers from 1 to n, and the columns — from left to right with numbers from 1 to m. Let’s denote the cell at the intersection of the r-th row and the c-th column as (r,?c).

Initially the pig stands in cell (1,?1), and in the end she wants to be in cell (n,?m). Since the pig is in a hurry to get home, she can go from cell (r,?c), only to either cell (r?+?1,?c) or (r,?c?+?1). She cannot leave the forest.

The forest, where the pig is, is very unusual. Some cells of the forest similar to each other, and some look very different. Peppa enjoys taking pictures and at every step she takes a picture of the cell where she is now. The path through the forest is considered to be beautiful if photographs taken on her way, can be viewed in both forward and in reverse order, showing the same sequence of photos. More formally, the line formed by the cells in order of visiting should be a palindrome (you can read a formal definition of a palindrome in the previous problem).

Count the number of beautiful paths from cell (1,?1) to cell (n,?m). Since this number can be very large, determine the remainder after dividing it by 109?+?7.

Input

The first line contains two integers n,?m (1?≤?n,?m?≤?500) — the height and width of the field.

Each of the following n lines contains m lowercase English letters identifying the types of cells of the forest. Identical cells are represented by identical letters, different cells are represented by different letters.

Output

Print a single integer — the number of beautiful paths modulo 109?+?7.

Sample test(s)

input

3 4

aaab

baaa

abba

output

3

题意概述:在一个n*m的矩阵中,每个格子都有一个字母。你从(1,1)出发前往(n,m),每次仅仅能向下或向右。当到达终点时,把你经过的字母写下来。产生一个字符串。求有多少种走成回文的方案。

每一次仅仅能向下或向右。所以考虑能够用dp做。考虑曼哈顿距离

按距离原点和终点的曼哈顿距离同样的两个点做状态转移

想象有两个点分别从起点和终点同一时候向中间走

用f[p1][p2] 表示 第一个点在p1位置第二个点在p2位置时的从起点终点同一时候走过的同样字母路径的合法状态数

f[p1][p2]=f[p1_f1][p2_f1]+f[p1_f1][p2_f2]+f[p1_f2][p2_f1]+f[p1_f2][p2_f2]

p1_f1,p1_f2,p2_f1,p2_f2分别表示p1和p2的前驱点

因为坐标非常大,须要用滚动数组优化。斜着循环每个点也须要一些小技巧详细看代码~

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
using namespace std;
const int MAX=505;
const int MOD=1e9+7;
char s[MAX][MAX];
int f[MAX][MAX]={0};
int f_[MAX][MAX];
int i,j,m,n,k,dis;
struct point{
    int x,y;
};
point next_1(point a)
{
    if (a.y==1&&a.x<n)
        a.x++;
    else
        a.y++;
    return a;
}
point next_2(point a)
{
    if (a.x==n&&a.y>1)
        a.y--;
    else
        a.x--;
    return a;
}
point nex(point a)
{
    a.x--;
    a.y++;
    return a;
}
int main()
{
    cin>>n>>m;
    int ans=0;
    getchar();
    for (i=1;i<=n;i++)
    {
        for (j=1;j<=m;j++)
            scanf("%c",&s[i][j]);
        getchar();
    }
    point a,b,p1,p2;
    a.x=a.y=1;
    b.x=n;b.y=m;
    int max_=(m+n)/2;
    if (s[1][1]==s[n][m])
        f[1][n]=1;
    else
        f[1][n]=0;
    if (m+n<=3)
    {
        cout<<f[1][n]<<endl;
        return 0;
    }
    for (dis=2;dis<=max_;dis++)
    {
        a=next_1(a);
        b=next_2(b);
        for (i=1;i<=500;i++)
            for (j=1;j<=500;j++)
            {
                f_[i][j]=f[i][j];
                f[i][j]=0;
            }
        for (p1=a;p1.y<=m&&p1.x>=1;p1=nex(p1))
            for (p2=b;p2.y<=m&&p2.x>=1;p2=nex(p2))
                if (s[p1.x][p1.y]==s[p2.x][p2.y])
                {
                    f[p1.x][p2.x]=((f_[p1.x-1][p2.x]+f_[p1.x-1][p2.x+1])%MOD+(f_[p1.x][p2.x]+f_[p1.x][p2.x+1])%MOD)%MOD;
                    if (((p1.x==p2.x)&&(abs(p1.y-p2.y)<=1))||((p1.y==p2.y)&&(abs(p1.x-p2.x)<=1)))
                        ans=(ans+f[p1.x][p2.x])%MOD;
                }
    }
    cout<<ans<<endl;
    return 0;
}

贴一个cf上看到的位运算的程序,相当简短

#include <bits/stdc++.h>
using namespace std;
#define f(i,n) for(int i=0;i<(n);i++)
#define fr(i,n) for(int i=n;i--;)
char x[500][501];
int d[2][501][501],n,m;
main(){
    cin>>n>>m;
    f(i,n) cin>>x[i];
    f(ei,n) fr(si,n) fr(sj,m){
        auto& c=d[ei&1][si][sj]=0,ej=n+m-2-si-sj-ei;
        if(si<=ei&&sj<=ej&&x[si][sj]==x[ei][ej]&&!(c=abs(si-ei)+abs(sj-ej)<=1))
            f(i,2) f(j,2) c=(c+d[ei-!j&1][si+!i][sj+!!i])%((int)1e9+7);
    }
    cout<<d[~n&1][0][0]<<‘\n‘;
}
时间: 2024-08-10 00:04:31

CF 316div2 E.Pig and Palindromes的相关文章

Codeforces Round #316 (Div. 2)E. Pig and Palindromes DP

E. Pig and Palindromes Peppa the Pig was walking and walked into the forest. What a strange coincidence! The forest has the shape of a rectangle, consisting of n rows and m columns. We enumerate the rows of the rectangle from top to bottom with numbe

Codeforces #316 E Pig and Palindromes DP

// Codeforces #316 E Pig and Palindromes // // 题目大意: // // 给你一张地图,n*m每个点是一个字母,现在从(0,0)出发, // 每次只能往右或者往下走,求走到(n-1,m-1)形成回文串的方法数. // // 解题思路: // // 动态规划.首先.如果起点和终点的字母不相同,那么肯定 // 不能形成回文串,直接输出0.对于能形成回文串.我们设状态 // d(step,i,j)表示走了step步,从第0行走到i行,第n-1行走到j行的 /

codeforces 570 E. Pig and Palindromes (DP)

题目链接: 570 E. Pig and Palindromes 题目描述: 有一个n*m的矩阵,每个小格子里面都有一个字母.Peppa the Pig想要从(1,1)到(n, m).因为Peppa the Pig是一个完美主义者,她想要她所经过的路径上的字母组成的字符串是一个回文串,现在Peppa the Pig想要知道有多少满足条件的走法? 解题思路: 因为经过路径上的字母要组成回文串,所以可以从(1,1),(n,m)同时开始dp.从(1,1)出发只能向下方和右方走,从(n,m)出发只能向上

codeforces 570 E. Pig and Palindromes

题意:给出n*m的字母表,求从左上角走到右下角能形成多少个回文串,只能往下或往右走. 做法:dp[r1][c1][r2][c2],从左上角走到(r1,c1),从右下角走到(r2,c2)时,能形成多少个回文串,因为爆内存,表示成dp[step][r1][r2],从左上角走到r1行,从右下角走到r2行,分别走了step步时,能形成多少个回文串,因为c1=step+2-r1,c2=n+m-step-r2,所以是一样的,这样差不多能过了,因为两边最多走250步,所以需要的空间是250*500*500,当

CF 464 A No to Palindromes! 找到最后靠后的可变字母;

A. No to Palindromes! time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output Paul hates palindromes. He assumes that string s is tolerable if each its character is one of the first p letters of th

Codeforces 570E Pig and Palindromes dp

链接 题解链接:点击打开链接 题意: 给定n*m的字母矩阵. 从左上角到右下角的路径中有多少条是回文. 思路: 显然是要从头尾同时dp的,路径1是从左上角到第j行,路径2是从右下角到第k行 dp[i][j][k] 表示路径长度为i,路径1从左上角到第j行,路径2从右下角到第k行,且路径1和2是匹配的方法数. 对于路径1.2合并时要分一下奇偶. #pragma comment(linker, "/STACK:1024000000,1024000000") #include <std

【动态规划】Codeforces 570E Pig and Palindromes

通道 题意:n*m的字符矩阵,每次只能向右或向下走,求从(1,1)走到(n,m)的路径中有多少个回文. 思路:我们可以从两端开始走.dp[i][j][k]:分别走i步(也可以理解为半径为i),A到达j行,B到达k行的回文数.走的半径逐渐减少即可. 可以知道第x1行只能由前一次的x1行或x1+1行转移,第x2行只能由前一次的x2-1或x2转移,所有就有四种情况. dp[i][x1][x2] = (dp[i-1][x1][x2] + dp[i-1][x1 + 1][x2] + dp[i-1][x1]

CF570E Pig and Palindromes

完全不会这种类型的$dp$啊…… 考虑回文串一定是可以拆分成(偶数个字母 + 偶数个字母)或者(偶数个字母 + 一个字母 +偶数个字母),两边的偶数个字母其实是完全对称的.因为这道题回文串的长度是给定的$n + m$,所以回文串的类型也是确定的. 发现直接$dp$不好转移,我们可以把走的步数拆成两半,从$(1, 1)$开始走$(n + m) / 2$步,从$(n, m)$开始走$(n + m) / 2$步,然后在中间相遇就可以计算答案,这样子只要每一次走到相同的格子就可以转移了. 我们先设计出一

CF#316 Div.2

第一篇文章 额大半夜 爬起来做CF SB题被hack之后还是错 没药救了 E题再有3分钟就调出来了 读题要认真,写题要专心QAQ Problem A Elections #include <cstdio> #include <algorithm> using namespace std; #pragma warning (disable : 4996) int f[101]; int main() { int n, m; scanf("%d%d", &n