[POI2012] OKR-A Horrible Poem 题解
前言
题目链接:洛谷。
题意简述
给出长度为 \(n\)(\(n \leq 5 \times 10^5\))的字符串 \(\texttt{S}\),\(q\)(\(q \leq 2 \times 10^6\))询问某一子串的最短循环节。\(\texttt{A}\) 是 \(\texttt{B}\) 的循环节,当 \(\texttt{B}\) 可以由 \(\texttt{A}\) 重复若干次拼接成。
题目分析
联想到 KMP 求循环节的过程,如果 \(len\) 是 \(\texttt{S}\) 的循环节长度,那么一定有 \(\texttt{S}[1 \ldots n - len] = \texttt{S}[len + 1 \ldots n]\),以及 \(len \mid n\)。当然,只有本身为循环节的话,\(len = n\)。
字符串相等的过程,可以用字符串哈希 \(\Theta(n) \sim \Theta(1)\) 地搞。但是枚举 \(len\) 是 \(\Theta(\sqrt{n})\) 的,时间复杂度 \(\Theta(n + q \sqrt{n})\),考虑优化。
发现 \(q\) 不和 \(n\) 同阶,所以想到预处理出每个数的所有因子。预处理用埃氏筛,时间复杂度 \(\Theta(n \log n)\),枚举的时候取决于因子最多的个数,记为 \(k\),在本题数据范围,应为 \(k = 200\)。时间复杂度 \(\Theta(n \log n + qk)\),能过本题。
当然还可以继续优化。
发现,如果 \(len\) 是答案,那么 \(k \cdot len\) 肯定也是答案。而 \(len = n\) 肯定是答案。所以考虑反过来计算。
即,将 \(n\) 分解成 \(\prod p_i ^ {k_i}\),答案 \(len = \prod p_i ^ {k'_i}\)。初始 \(k'_i = k_i\),那么每次就是尝试将一个 \(k'_i \gets k'_i - 1\),如果得到的 \(len\) 是一个合法循环节长度,那就减掉。
由于 \(k'_i\) 之间互不影响,从小的质因数开始尝试。记 \(f(x)\) 表示 \(x\) 的最小质因数。设 \(ans = len\),然后循环判断 \(\cfrac{ans}{f(len)}\) 能否成为新的答案,可以就让 \(ans \gets \cfrac{ans}{f(len)}\)。然后 \(len \gets \cfrac{len}{f(len)}\)。直到 \(len = 1\)。这里 \(len\) 和 \(f(len)\) 就是在不断搞最小质因数的过程。
预处理可以用线性筛 \(\Theta(n)\) 地搞。查询的时候,时间复杂度是 \(\Theta(\sum k_i) \leq \mathcal{O}(\log n)\)。总的时间复杂度 \(\mathcal{O}(n + q \log n)\)。
代码
略去了快读快写。卡卡常最优解。
#include <cstdio>
#include <algorithm>
using namespace std;
int n, q;
char str[500010];
using ull = unsigned long long;
int hav[500010], pri[500010], pcnt;
ull hsh[500010], pw[500010];
inline ull get_hash(int l, int r) {
if (l > r) return 0;
return hsh[r] - hsh[l - 1] * pw[r - l + 1];
}
signed main() {
fread(buf, 1, MAX, stdin);
read(n);
for (int i = 2; i <= n; ++i) {
if (!hav[i]) hav[i] = pri[++pcnt] = i;
for (int j = 1; j <= pcnt && i * pri[j] <= n; ++j) {
hav[i * pri[j]] = pri[j];
if (i % pri[j] == 0) break;
}
}
pw[0] = 1;
for (register int i = 1; i <= n; ++i) {
do str[i] = getchar(); while (str[i] < 'a' || str[i] > 'z');
hsh[i] = (hsh[i - 1] * 131 + str[i] - 'a' + 11);
pw[i] = pw[i - 1] * 131;
}
read(q);
for (register int i = 1, l, r; i <= q; ++i) {
read(l), read(r);
if (get_hash(l, r - 1) == get_hash(l + 1, r)) {
write(1), putchar('\n');
continue;
}
int ans = r - l + 1, len = ans;
while (len > 1) {
if (get_hash(l, r - ans / hav[len]) == get_hash(l + ans / hav[len], r))
ans /= hav[len];
len /= hav[len];
}
write(ans), putchar('\n');
}
fwrite(obuf, 1, o - obuf, stdout);
return 0;
}
本文作者:XuYueming,转载请注明原文链接:https://www.cnblogs.com/XuYueming/p/18326726。
若未作特殊说明,本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。