2025.05.13 CW 模拟赛 C. 折纸
C. 折纸
题目描述
思路
先考虑一下部分分, 也就是只有 \(1\) 行的情况.
思考一下折叠的本质: 这个操作事实上就是将某一个回文前缀/后缀的一半删除, 同时这个回文的长度一定是偶数. 例如 abbac, 其前缀有回文 abba, 折叠一下就成了 bac, 如果后缀有也同理.
前缀和后缀是等价的, 我们就讨论一下前缀如何处理. 我们定义 \(f_i\) 表示是否存在一种操作, 使得 \([1, i)\) 的字符被删除, 亦即保留 \([i, n]\). 初始化 \(f_1 = 1\). 考虑转移, 我们记 \(p_i\) 表示以 \(i - 1, i\) 为回文中心的最长回文半径, 显然对于半径 \(r \in [1, p_i]\) 的字串一定都是回文的, 也就是能够从 \(f_{i - r}\) 转移过来, 我们记一个前缀即可 \(\mathcal{O}(1)\) 转移.
最后统计答案, 相当于统计有多少个 \([l, r]\) 是合法的, 即 \(l \le r, f_l = 1, g_r = 1\). 我们定义 \(suf = \sum g_i\), 接着从前往后扫, \(ans := ans + f_i \times suf, suf := suf - g_i\).
上面的讨论都是基于只有 \(1\) 行的情况, 但是对于矩阵同样适用. 具体来说, 我们将每一列/行哈希成一个数值, 然后就成了 \(1\) 行的情况, 最后将行和列的答案乘起来即可.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
typedef unsigned long long ull;
constexpr int N = 1000010;
constexpr ull P = 131;
int n, m, p[N << 1];
vector<string> s;
vector<ull> deal(vector<ull> v) {
    vector<ull> res;
    res.push_back(0), res.push_back(1);
    for (ull x : v) {
        res.push_back(x), res.push_back(1);
    }
    res.push_back(0);
    return res;
}
void manacher(vector<ull> v) {
    int r = 0, c = 0, sz = v.size() - 1;
    for (int i = 1; i <= sz; ++i) {
        if (i < r) {
            p[i] = min(p[c * 2 - i], p[c] + c - i);
        }
        else {
            p[i] = 1;
        }
        while (v[i + p[i]] == v[i - p[i]]) {
            ++p[i];
        }
        if (p[i] + i > r) {
            r = p[i] + i, c = i;
        }
    }
}
class Row {
private:
    int f[N], g[N];
public:
    ull calc() {
        vector<ull> H;
        for (int i = 1; i <= m; ++i) {
            ull num = 0;
            for (int j = 1; j <= n; ++j) {
                num = num * P + s[j][i];
            }
            H.push_back(num);
        }
        H = deal(H), manacher(H);
        f[1] = g[m] = 1;
        for (int i = 2; i <= m; ++i) {
            int len = (p[i * 2 - 1] - 1) / 2;
            f[i] = f[i - 1] + (f[i - 1] - f[i - len - 1] > 0);
        }
        for (int i = m - 1; i; --i) {
            int len = (p[i * 2 + 1] - 1) / 2;
            g[i] = g[i + 1] + (g[i + 1] - g[i + len + 1] > 0);
        }
        
        ull res = 0;
        for (int i = 1; i <= m; ++i) {
            res += (f[i] - f[i - 1]) * g[i];
        }
        return res;
    }
} row;
class Column {
private:
    int f[N], g[N];
public:
    ull calc() {
        vector<ull> H;
        for (int i = 1; i <= n; ++i) {
            ull num = 0;
            for (int j = 1; j <= m; ++j) {
                num = num * P + s[i][j];
            }
            H.push_back(num);
        }
        H = deal(H), manacher(H);
        f[1] = g[n] = 1;
        for (int i = 2; i <= n; ++i) {
            int len = (p[i * 2 - 1] - 1) / 2;
            f[i] = f[i - 1] + (f[i - 1] - f[i - len - 1] > 0);
        }
        for (int i = n - 1; i; --i) {
            int len = (p[i * 2 + 1] - 1) / 2;
            g[i] = g[i + 1] + (g[i + 1] - g[i + len + 1] > 0);
        }
        
        ull res = 0;
        for (int i = 1; i <= n; ++i) {
            res += (f[i] - f[i - 1]) * g[i];
        }
        return res;
    }
} column;
void init() {
    cin >> n >> m;
    s.resize(n + 1);
    for (int i = 1; i <= n; ++i) {
        cin >> s[i], s[i] = ' ' + s[i];
    }
}
void calculate() {
    cout << row.calc() * column.calc() << '\n';
}
void solve() {
    init();
    calculate();
}
int main() {
    solve();
    return 0;
}
总结
多在题目中找性质. 对于字符串矩阵, 在某些情况下可以哈希成一行进行处理.
                    
                

                
            
        
浙公网安备 33010602011771号