Codeforces 1080E - Sonya and Matrix Beauty - Manacher

140 篇文章 0 订阅
80 篇文章 0 订阅

Codeforces 1080E - Sonya and Matrix Beauty - Manacher

题解链接

https://lucien.ink


题目链接

https://codeforces.com/contest/1080/problem/E


题目

Sonya had a birthday recently. She was presented with the matrix of size n × m n\times m n×m and consist of lowercase Latin letters. We assume that the rows are numbered by integers from 1 1 1 to n n n from bottom to top, and the columns are numbered from 1 1 1 to m m m from left to right.

Let’s call a submatrix ( i 1 , j 1 , i 2 , j 2 ) (i_1, j_1, i_2, j_2) (i1,j1,i2,j2) ( 1 ≤ i 1 ≤ i 2 ≤ n ; 1 ≤ j 1 ≤ j 2 ≤ m ) (1\leq i_1\leq i_2\leq n; 1\leq j_1\leq j_2\leq m) (1i1i2n;1j1j2m) elements a i j a_{ij} aij of this matrix, such that i 1 ≤ i ≤ i 2 i_1\leq i\leq i_2 i1ii2 and j 1 ≤ j ≤ j 2 j_1\leq j\leq j_2 j1jj2. Sonya states that a submatrix is beautiful if we can independently reorder the characters in each row (not in column) so that all rows and columns* of this submatrix form palidroms.

Let’s recall that a string is called palindrome if it reads the same from left to right and from right to left. For example, strings a b a c a b a , b c a a c b , a abacaba, bcaacb, a abacaba,bcaacb,a are palindromes while strings a b c a , a c b b a , a b abca, acbba, ab abca,acbba,ab are not.

Help Sonya to find the number of beautiful submatrixes. Submatrixes are different if there is an element that belongs to only one submatrix.


题意

  定一个子矩阵是完美的,当且仅当任意重排列这个字矩阵的每一行后,矩阵的每一行和每一列都是一个回文串,问给定一个 n ⋅ m n \cdot m nm 的矩阵,有多少个完美的子矩阵。


思路

  首先观察一下,如果某一行能够变成回文串,当且仅当这一行奇数字母的个数小于 2 2 2。现在我们假设某个子矩形的每一行都可以形成回文串,那么怎么判断纵向的呢?

  可以注意到,回文串的第一个字符等于最后一个字符,而题目里要求子矩阵的每一列都是回文串,也就是说第一行中的每一个字母都要在最后一行里找到一个相同的字母,因为每一行都可以任意排列,所以满足上述条件的充要条件就是这两行之间所有的字母出现的次数都相同,此时我们定义这两行是全等的。进一步推广,第二行全等于倒数第二行 … \dots i i i 行全等于第 n − i + 1 n - i + 1 ni+1 行,我们发现如果把这个子矩阵的每一行看作一个字符的话,对于单个子矩阵来说,问题无非就变成了给你一个长度为 n n n 的字符串,统计里面有多少个回文串,这个问题我们可以 O ( n ) O(n) O(n) 通过 M a n a c h e r Manacher Manacher 来求解。

  于是我们便有了思路,首先 m 2 m ^ 2 m2 枚举左右边界,对于一个固定的左右边界,通过 M a n a c h e r Manacher Manacher 进行回文计数,而由于在 M a n a c h e r Manacher Manacher 中判断两个“字符”相等时其实是扫了一遍 26 26 26 个字母,所以我们当前做法的复杂度为 O ( 26 ⋅ m 2 ⋅ n ) O(26 \cdot m ^ 2 \cdot n) O(26m2n) 的,由于 n n n m m m 同数量级,故简写为 O ( 26 ⋅ n 3 ) O(26 \cdot n ^ 3) O(26n3),通过计算发现其复杂度约为 4 ⋅ 1 0 8 4 \cdot 10 ^ 8 4108,不是很优秀,写的稍微丑一点就会 TLE

  此时我们可以通过 花式 哈希来去掉 26 26 26 这个常数,复杂度就可以进化到 O ( n 3 ) O(n ^ 3) O(n3),约为 1.5 ⋅ 1 0 7 1.5 \cdot 10 ^ 7 1.5107,这道题给了 1.5 s e c 1.5sec 1.5sec,显然已经足够了。

  貌似是有 O ( n 2 ⋅ l o g 2 ( n ) ) O(n ^ 2 \cdot log_2(n)) O(n2log2(n)) 的解法?本 juruo 学识短浅,还请各位 dalao 多多指教 Orz


实现

https://pasteme.cn/2340

#include <bits/stdc++.h>
const int maxn = 257;
char map[maxn][maxn];
int n, m, mask[maxn], e[maxn], ans, mod = 19260817, base = 257;
struct Manacher {
    int p[maxn << 1], str[maxn << 1];
    bool check(int x) {return x - (x & -x) == 0;}
    bool equal(int x, int y) {
        if ((x & 1) != (y & 1)) return false;
        return (x & 1) || (check(mask[x >> 1]) && check(mask[y >> 1]) && str[x] == str[y]);
    }
    int solve(int len) {
        int max_right = 0, pos = 0, ret = 0;
        for (int i = 1; i <= len; i++) {
            p[i] = (max_right > i ? std::min(p[pos + pos - i], max_right - i) : 1);
            if ((i & 1) || check(mask[i >> 1])) {
                while (equal(i + p[i], i - p[i])) p[i]++;
                if (max_right < i + p[i]) pos = i, max_right = i + p[i];
                ret += p[i] / 2;
            } else p[i] = 2;
        }
        return ret;
    }
} manacher;
int main() {
    scanf("%d%d", &n, &m);
    manacher.str[0] = -1, manacher.str[n * 2 + 2] = -2, e[0] = 1;
    for (int i = 1; i <= 26; i++) e[i] = int(1ll * e[i - 1] * base % mod);
    for (int i = 1; i <= n; i++) scanf("%s", map[i] + 1);
    for (int l = 1; l <= m; l++) {
        for (int i = 1; i <= n; i++) mask[i] = manacher.str[i << 1] = 0;
        for (int r = l; r <= m; r++) {
            for (int i = 1; i <= n; i++) {
                mask[i] ^= (1 << (map[i][r] - 'a'));
                manacher.str[i << 1] = (manacher.str[i << 1] + e[map[i][r] - 'a' + 1]) % mod;
            }
            ans += manacher.solve(n << 1 | 1);
        }
    }
    printf("%d\n", ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值