[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;
}
posted @ 2024-07-27 19:15  XuYueming  阅读(19)  评论(0编辑  收藏  举报