题解 | 2023 年牛客多校第三场 G 题 Beautiful Matrix

Beautiful Matrix

https://ac.nowcoder.com/acm/contest/57357/G

题意:给定一个 n×mn\times m 的字符矩阵 {S}(i,j)=(1,1)(n,m)\{S\}_{(i,j)=(1,1)}^{(n,m)},定义一个 n×nn\times n 的子矩阵是优美的当且仅当 i,j[1,n]\forall i,j \in [1,n]Si,j=Sni+1,nj+1S_{i,j}=S_{n-i+1,n-j+1}。问 {S}\{S\} 中有多少个优美的子矩阵。1n,m2×1031\le n,m \le 2\times 10^3

解法:本题卡常,请使用 O(n2)\mathcal O(n^2) 的做法通过。

首先枚举哪一行是中央对称行。当固定中央行之后,考虑在这一行做 Manacher。传统的 Manacher 是仅单字符匹配成立即可扩展回文半径,在本题这种矩阵对称中,可以考虑对一个矩阵区域(正向绿色等于反向紫色)的匹配作为扩展条件:

alt

因而使用二维哈希判断子矩阵对称相等,结合 Manacher 算法即可通过。复杂度同每行 Manacher 算法,即 O(nm)O(nm)

本题严重卡常。

#include <bits/stdc++.h>
using namespace std;
const int N = 2000;
int a[N + 5][N + 5], n, m;
struct Hash1
{
    const int p1 = 31, p2 = 1331, mod = 1e9 + 9;
    void add(int &x, int y)
    {
        if ((x += y) >= mod)
            x -= mod;
    }
    void sub(int &x, int y)
    {
        if ((x -= y) < 0)
            x += mod;
    }
    int invth1[N + 5], invth2[N + 5];
    int Pow1[N + 5], Pow2[N + 5];
    int sum1[N + 5][N + 5], sum2[N + 5][N + 5];
    int power(int a, int x)
    {
        int ans = 1;
        while (x)
        {
            if (x & 1)
                ans = 1ll * ans * a % mod;
            a = 1ll * a * a % mod;
            x >>= 1;
        }
        return ans;
    }
    int inv(int a)
    {
        return power(a, mod - 2);
    }
    void init()
    {
        Pow1[0] = Pow2[0] = invth1[0] = invth2[0] = 1;
        int invp1 = inv(p1), invp2 = inv(p2);
        for (int i = 1; i < N + 5; i++)
        {
            Pow1[i] = 1ll * Pow1[i - 1] * p1 % mod;
            Pow2[i] = 1ll * Pow2[i - 1] * p2 % mod;
            invth1[i] = 1ll * invth1[i - 1] * invp1 % mod;
            invth2[i] = 1ll * invth2[i - 1] * invp2 % mod;
        }
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
            {
                sum1[i][j] = 1ll * Pow1[i] * Pow2[j] % mod * a[i][j] % mod;
                sum2[i][j] = 1ll * Pow1[n - i + 1] * Pow2[m - j + 1] % mod * a[i][j] % mod;
            }
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
            {
                add(sum1[i][j], sum1[i - 1][j]);
                add(sum1[i][j], (sum1[i][j - 1] - sum1[i - 1][j - 1] + mod) % mod);
                add(sum2[i][j], sum2[i - 1][j]);
                add(sum2[i][j], (sum2[i][j - 1] - sum2[i - 1][j - 1] + mod) % mod);
            }
    }
    bool check(int l, int r, int u, int d)
    {
        int g1 = 0;
        add(g1, sum1[u][d]);
        sub(g1, sum1[l - 1][d]);
        sub(g1, sum1[u][r - 1]);
        add(g1, sum1[l - 1][r - 1]);
        int g2 = 0;
        add(g2, sum2[u][d]);
        sub(g2, sum2[l - 1][d]);
        sub(g2, sum2[u][r - 1]);
        add(g2, sum2[l - 1][r - 1]);
        g1 = 1ll * g1 * invth1[l - 1] % mod * invth2[r - 1] % mod;
        g2 = 1ll * g2 * invth1[n - u] % mod * invth2[m - d] % mod;
        return g1 == g2;
    }
};
Hash1 h1;
struct Hash2
{
    const int p1 = 131, p2 = 1331, mod = 1e9 + 9;
    void add(int &x, int y)
    {
        if ((x += y) >= mod)
            x -= mod;
    }
    void sub(int &x, int y)
    {
        if ((x -= y) < 0)
            x += mod;
    }
    int invth1[N + 5], invth2[N + 5];
    int Pow1[N + 5], Pow2[N + 5];
    int sum1[N + 5][N + 5], sum2[N + 5][N + 5];
    int power(int a, int x)
    {
        int ans = 1;
        while (x)
        {
            if (x & 1)
                ans = 1ll * ans * a % mod;
            a = 1ll * a * a % mod;
            x >>= 1;
        }
        return ans;
    }
    int inv(int a)
    {
        return power(a, mod - 2);
    }
    void init()
    {
        Pow1[0] = Pow2[0] = invth1[0] = invth2[0] = 1;
        int invp1 = inv(p1), invp2 = inv(p2);
        for (int i = 1; i < N + 5; i++)
        {
            Pow1[i] = 1ll * Pow1[i - 1] * p1 % mod;
            Pow2[i] = 1ll * Pow2[i - 1] * p2 % mod;
            invth1[i] = 1ll * invth1[i - 1] * invp1 % mod;
            invth2[i] = 1ll * invth2[i - 1] * invp2 % mod;
        }
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
            {
                sum1[i][j] = 1ll * Pow1[i] * Pow2[j] % mod * a[i][j] % mod;
                sum2[i][j] = 1ll * Pow1[n - i + 1] * Pow2[m - j + 1] % mod * a[i][j] % mod;
            }
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
            {
                add(sum1[i][j], sum1[i - 1][j]);
                add(sum1[i][j], (sum1[i][j - 1] - sum1[i - 1][j - 1] + mod) % mod);
                add(sum2[i][j], sum2[i - 1][j]);
                add(sum2[i][j], (sum2[i][j - 1] - sum2[i - 1][j - 1] + mod) % mod);
            }
    }
    bool check(int l, int r, int u, int d)
    {
        int g1 = 0;
        add(g1, sum1[u][d]);
        sub(g1, sum1[l - 1][d]);
        sub(g1, sum1[u][r - 1]);
        add(g1, sum1[l - 1][r - 1]);
        int g2 = 0;
        add(g2, sum2[u][d]);
        sub(g2, sum2[l - 1][d]);
        sub(g2, sum2[u][r - 1]);
        add(g2, sum2[l - 1][r - 1]);
        g1 = 1ll * g1 * invth1[l - 1] % mod * invth2[r - 1] % mod;
        g2 = 1ll * g2 * invth1[n - u] % mod * invth2[m - d] % mod;
        return g1 == g2;
    }
};
Hash2 h2;
bool check(int a, int b, int c, int d)
{
    if (!(a >= 1 && a <= n && b >= 1 && b <= m && c >= 1 && c <= n && d >= 1 && d <= m && a <= c && b <= d))
        return false;
    return h1.check(a, b, c, d) && h2.check(a, b, c, d);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m;
    string s;
    for (int i = 1; i <= n; i++)
    {
        cin >> s;
        for (int j = 1; j <= m; j++)
            a[i][j] = s[j - 1] - 'a' + 1;
    }
    h1.init();
    h2.init();
    long long ans = 0;
    for (int z = 1; z <= n; z++)
    {
        int mx = 0, id = 1;
        vector<int> para(m + 1);
        for (int i = 1; i <= m; i++)
        {
            if (mx > i)
                para[i] = min(mx - i, para[2 * id - i]);
            else
                para[i] = 1;
            while (check(z - para[i] + 1, i - para[i] + 1, z + para[i] - 1, i + para[i] - 1))
                para[i]++;
            para[i]--;
            if (para[i] + i > mx)
            {
                mx = para[i] + i;
                id = i;
            }
            ans += para[i];
        }
    }
    for (int z = 1; z < n; z++)
    {
        int mx = 0, id = 1;
        vector<int> para(m + 1);
        for (int i = 1; i < m; i++)
        {
            if (mx > i)
                para[i] = min(mx - i, para[2 * id - i]);
            else
                para[i] = 1;
            while (check(z - para[i] + 1, i - para[i] + 1, z + para[i], i + para[i]))
                para[i]++;
            if (para[i] > 0)
                para[i]--;
            if (para[i] + i > mx)
            {
                mx = para[i] + i;
                id = i;
            }
            ans += para[i];
        }
    }
    cout << ans;
    return 0;
}
全部评论
为什么manacher有两种状态呢,还是没有太明白
点赞 回复 分享
发布于 2023-07-26 19:57 内蒙古
为什么第一个循环是<=而第二个循环是<啊,而且为什么if (para[i] > 0) para[i]--;而第一个循环没有呢?
点赞 回复 分享
发布于 2023-07-26 19:06 内蒙古

相关推荐

头像
01-12 14:44
已编辑
百度_高级研发工程师
今天看到了某平台攻击牛友的帖子,段段今天打算为牛友们说句话,我们的努力到底有没有意义。&nbsp;(原文复述:感觉牛客就是当年那群做题区毕业了开始找工作还收不住那股味,颇有一种从年级第一掉到年纪第二后抱怨考不上大学的区味)&nbsp;&nbsp;粗鄙,无礼,傲慢,攻击,在这里我没有看到任何有用的分析,我只看到了屁股决定脑袋的攻击,我只看到了嫉妒和眼红。一、去医院不看病你去逛街吗&nbsp;去医院你不去看病你去逛街吗?去加油站不加油你去抽烟吗?去部队你不训练战斗技能你去养老吗?来牛客你不努力求职你来干什么来了。&nbsp;牛客本身就是个求职平台,大家分享有用的知识,分享面经,分享offer,分享求职经验的,来牛客不就干这个来了吗?有什么问题吗?...
给个好点的工作吧啊啊...:不知道我看的是不是和博主同样的帖子,我记得原帖是表达的是有些匿名老是发几十万的offer侮辱价,然后就有牛友觉得凡尔赛了导致后面的评论有些偏激。我觉得这个最近闫学晶那个事情有点类似了,她说他儿子一年只能赚七八十万家庭生活都难以为继,不说普通家庭,多少大厂的程序员都赚不到这个数字,大部分家庭看到这种发言肯定会难受的一p,生活的担子又这么重,人都是需要发泄情绪的,互联网就是个极佳的载体,所以很多人直接就喷她了,人在情绪发泄的时候是不思考的,否则就不叫发泄了。然后还有一个点,段哥假定了这些喷的人全都是“躺平的”,这点可能有失偏颇,很多人一直在努力,但是始终缺乏天时地利人和的某一个条件,这点相信段哥找工作的过程中深有体会。绝大部分人都以结果的失败去否认了努力的全过程,可能只是别人努力的方向错了。就像一次面试,可能你准备了很久,结果面试官就是比较奇葩,一直问没有学习到的领域或知识点,然后有人凭一个挂掉的结果就直接给你扣了一个“躺平”的帽子,觉得挂掉是你不够努力,您心里滋味如何?再说点近点的,我也是od,多少同事深夜无偿加班,涨过一分工资吗?多少外包的技术大牛因为学历被困在外包,连od都进不去,这些人难道不努力吗?只是限制与生活、公司制度等等之类的无奈罢了。说到努力,又想到李家琦79元眉笔事件,这么多年有没有认真工作?有没有涨工资?他嘴里说出来是那么的理所当然,打工牛马都知道“任劳任怨”,“认真工作”真能涨工资?只干活不发声就等着被摘果子吧,企业里永远都是“汇报杰出者”升的最快(当然不是所有企业),这种事情相信段哥包括我甚至大部分od都经历过。最近辞职回老家,和老爸散步每次他都会感慨街上的蔬菜小贩多不容易,他们晚上就窝在那种三轮小货车的驾驶室里,腿都伸不直,我们这里晚上零下了,只盖一条薄毛毯,始终舍不得住我们镇上几十块的酒店,因为一车蔬菜就赚几百块顶多一千而且要卖好久,这样的例子还有太多了。这种芸芸众生可能辛苦了一天之后,打开手机看到网上的凡尔赛发言,跟风喷了几句发泄情绪,我觉得这种人不应该扣上“躺平”的帽子。我觉得大部分正常人都是努力的,或者曾经努力过,但世界上有太多努力解决不了的无奈了,甚至说你都没有那个努力的机会,不过正因如此,才显得坚持不懈的努力奋斗之人的难得可贵,认清生活的真相后仍然热爱生活,敢于直面现实的淋漓。
段段STEADY觉醒与突...
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务