这个题刚开始胡了个假做法,导致做完整个题用了一整个下午,我果然还是太菜了……
题意
长度为 的串, 组询问,求区间本质不同平方串个数。
题解
首先有若干个 djq 说早就被普及了的结论,也不一定需要都用上。
引理 1(runs theorem): run 的个数不超过 。
证明大概是每个 run 的 lyndon 根不交。
引理 2: 每个位置结尾的本原平方串个数为 。
这个证明大概是,如果存在三个串 且 ,并且 是 的后缀, 是 的后缀,则 。具体细节好像比较困难,见 zzq 论文。
引理 3: 本质不同的本原平方串个数不超过 。
证明:若一个位置同时有 三个本原平方串以其为结尾且未在前面出现,由于 , ,因此 是 的后缀,已经出现过一次。因此每个位置最多新产生 个本原平方串。(听说这个引理可以推广到“本质不同的平方串个数不超过 ”,不过我不会证……)
接下来是做法。
因此就轻易获得了一个 的莫队做法,直接每个位置算出以他为结尾的本原平方串有哪些,暴力更新即可。(听说现场这个做法跑过去了)
不过还有一个比较明显的思路就是做扫描线。一个暴力的做法大概就是,假设当前扫到了位置 ,找出所有以 为结尾的平方串(没有本原),然后把它上次出现位置的权值 --,当前位置权值 ++,询问直接查询区间和即可。(听说数据比较水,所以这个 的做法剪剪枝就过了?)
不过这种做法可以稍加改进,就不用修改这么多位置了。djq 好像用的是某种数点,本蒟蒻试图理解但是失败了,只好自己 yy 了一个……
考虑为什么要修改以 结尾的所有平方串,这是因为有可能有若干 runs 经过了 (即当前扫到的点),如果我们不暴力更新就会 WA。所以我们先不考虑经过当前询问右端点的 runs,只计算其他 runs 对答案的贡献。同时,为了避免重复计算,我们还是需要动态维护只考虑 这个前缀时,每种平方串最后一次出现在哪个 runs 中。
现在就非常容易计算了,流程大致如下:
- 遍历以 结尾的所有本原平方串,找到其对应的以 结尾的极长平方串,更新该平方串最后一次的出现位置,并把上次出现位置的权值 --。
- 遍历右端点为 的所有询问,先在 BIT 上询问出不算经过右端点的 runs,答案是多少。然后遍历以 结尾的所有本原平方串,计算它对当前询问的贡献。假设有效区间长度为 ,周期为 ,那么贡献应该是 。这个东西随便讨论一下就可以 算了。
- 遍历所有右端点为 的 runs,把它包含的所有平方串的最后一次出现位置权值 ++。注意步骤 1 中已经去过重了,所以这里不需要去重。
这样就计算完了。考虑一下复杂度,建 SA,算 runs 视为 ,步骤 1 总共会枚举 次,步骤 2 会枚举 次,步骤 3 也可以视为对于每个结尾枚举其本原平方串,次数也是 ,加上 BIT,因此总复杂度为 。
不知道能不能更优,如果能做到单 的话请赐教 /kel
写博客时还是榜 rk1
没写调和级数算本原平方串,写的是 lyndon array 算 runs……
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
template<typename T> void chkmax(T &a, const T &b) { a = a > b ? a : b; }
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }
namespace IO {
const int MAXR = 10000000;
char _READ_[MAXR], _PRINT_[MAXR];
int _READ_POS_, _PRINT_POS_, _READ_LEN_;
inline char readc() {
#ifndef ONLINE_JUDGE
return getchar();
#endif
if (!_READ_POS_) {
if (feof(stdin)) return -1;
_READ_LEN_ = fread(_READ_, 1, MAXR, stdin);
}
char c = _READ_[_READ_POS_++];
if (_READ_POS_ == _READ_LEN_) _READ_POS_ = 0;
return c;
}
template<typename T> inline int read(T &x) {
x = 0; register int flag = 1, c;
while (((c = readc()) < '0' || c > '9') && c != '-')
if (c < 0) return -1;
if (c == '-') flag = -1; else x = c - '0';
while ((c = readc()) >= '0' && c <= '9') x = x * 10 - '0' + c;
x *= flag; return 0;
}
inline int read(char *s) {
register int len = 0, c;
while (isspace(c = readc()) || c <= 0)
if (c < 0) return -1;
s[len++] = c;
while (!isspace(c = readc()) && c) s[len++] = c;
s[len] = 0;
return len;
}
template<typename T1, typename ...T2> inline int read(T1 &a, T2&... x) {
return read(a) | read(x...);
}
inline void ioflush() { fwrite(_PRINT_, 1, _PRINT_POS_, stdout), _PRINT_POS_ = 0; fflush(stdout); }
inline void printc(char c) {
if (!c) return;
_PRINT_[_PRINT_POS_++] = c;
if (_PRINT_POS_ == MAXR) ioflush();
}
template<typename T> inline void print(T x, char c = '\n') {
if (x < 0) printc('-'), x = -x;
if (x) {
static char sta[20];
register int tp = 0;
for (; x; x /= 10) sta[tp++] = x % 10 + '0';
while (tp > 0) printc(sta[--tp]);
} else printc('0');
printc(c);
}
inline void print(char *s, char c = '\n') {
for (int i = 0; s[i]; i++) printc(s[i]);
printc(c);
}
inline void print(const char *s, char c = '\n') {
for (int i = 0; s[i]; i++) printc(s[i]);
printc(c);
}
template<typename T1, typename ...T2> inline void print(T1 x, T2... y) {
print(x, ' '), print(y...);
}
}
namespace HM {
static const int SIZE = 1 << 20, MOD = SIZE - 1;
LL key[SIZE]; int val[SIZE];
int hash(LL x) { return (x >> 43 ^ x >> 25 ^ x << 14 ^ x >> 7 ^ x) & MOD; }
int& get(LL x) {
int p = hash(x), s = (p & 7) | 1;
for (; key[p] && key[p] != x; p = (p + s) & MOD);
key[p] = x;
return val[p];
}
}
const int MAXN = 200005;
int n, m;
char str[MAXN];
namespace Hash {
const int MOD1 = 1004535809, MOD2 = 1000000009, BASE1 = 14, BASE2 = 35;
LL hsh1[MAXN], hsh2[MAXN], pw1[MAXN], pw2[MAXN];
LL get_hash(int l, int r) {
return --l, (hsh1[r] + (MOD1 - hsh1[l]) * pw1[r - l]) % MOD1 << 32 |
(hsh2[r] + (MOD2 - hsh2[l]) * pw2[r - l]) % MOD2;
}
int lcp(int a, int b) {
if (str[a] != str[b]) return 0;
int l = 0, r = min(n - b, n - a) + 1;
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (get_hash(a, a + mid) == get_hash(b, b + mid)) l = mid;
else r = mid;
}
return r;
}
int lcs(int a, int b) {
if (str[a] != str[b]) return 0;
int l = 0, r = min(a, b);
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (get_hash(a - mid, a) == get_hash(b - mid, b)) l = mid;
else r = mid;
}
return r;
}
void init() {
pw1[0] = pw2[0] = 1;
for (int i = 1; i <= n; i++) {
pw1[i] = pw1[i - 1] * BASE1 % MOD1;
pw2[i] = pw2[i - 1] * BASE2 % MOD2;
hsh1[i] = (hsh1[i - 1] * BASE1 + str[i]) % MOD1;
hsh2[i] = (hsh2[i - 1] * BASE2 + str[i]) % MOD2;
}
}
}
struct Runs {
int l, r, p;
bool operator<(const Runs &q) const {
return r == q.r ? p < q.p : r < q.r;
}
bool operator==(const Runs &q) const {
return r == q.r && p == q.p && l == q.l;
}
} runs[MAXN << 1];
int ans[MAXN], bit[MAXN], tot;
struct Qry { int l, id; };
vector<Qry> qry[MAXN];
vector<Runs> vr[MAXN];
vector<int> md[MAXN];
void add(int x, int y) {
for (; x <= n; x += x & -x) bit[x] += y;
}
int ask(int x) {
int s = 0;
for (; x; x -= x & -x) s += bit[x];
return s;
}
namespace SA {
int sa[MAXN], fst[MAXN], sec[MAXN], cnt[MAXN], lyn[2][MAXN];
int sta[2][MAXN];
int cmp(int a, int b, int l) {
return a + l * 2 > n + 1 || b + l * 2 > n + 1 ||
sec[a] != sec[b] || sec[a + l] != sec[b + l];
}
void init(char *s) {
for (int i = 1; i <= n; i++) ++cnt[fst[i] = s[i]];
int m = 128;
for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
for (int i = n; i > 0; i--) sa[cnt[fst[i]]--] = i;
for (int i = 1; i < n; i <<= 1) {
int p = 0;
for (int j = n - i + 1; j <= n; j++) sec[++p] = j;
for (int j = 1; j <= n; j++) if (sa[j] > i) sec[++p] = sa[j] - i;
memset(cnt, 0, sizeof(cnt));
for (int j = 1; j <= n; j++) ++cnt[fst[j]];
for (int j = 1; j <= m; j++) cnt[j] += cnt[j - 1];
for (int j = n; j > 0; j--) sa[cnt[fst[sec[j]]]--] = sec[j];
memcpy(sec, fst, sizeof(sec));
m = fst[sa[1]] = 1;
for (int j = 2; j <= n; j++)
fst[sa[j]] = (m += cmp(sa[j - 1], sa[j], i));
if (m > n) break;
}
sa[0] = n + 1;
for (int i = 0; i <= n; i++) fst[sa[i]] = i;
// for (int i = 1; i <= n; i++) printf("%d\n", sa[i]);
// init lyndon array
int tp[2] = { 0, 0 };
sta[0][++tp[0]] = 0, sta[1][++tp[1]] = n + 1;
sa[n + 1] = n + 1;
for (int i = n; i > 0; i--) {
while (tp[0] > 0 && fst[i] < sta[0][tp[0]]) --tp[0];
while (tp[1] > 0 && fst[i] > sta[1][tp[1]]) --tp[1];
if (fst[i + 1] > fst[i]) {
lyn[1][i] = 1;
lyn[0][i] = sa[sta[0][tp[0]]] - i;
} else {
lyn[0][i] = 1;
lyn[1][i] = sa[sta[1][tp[1]]] - i;
}
sta[0][++tp[0]] = sta[1][++tp[1]] = fst[i];
}
// calculate runs
int lst = 1;
for (int i = 1; i < n; i++) {
int x = max(lyn[0][i], lyn[1][i]);
assert(x > 1);
int l = Hash::lcs(i, i + x), r = Hash::lcp(i, i + x) + x;
if (l + r - 1 >= 2 * x)
runs[++tot] = Runs { i - l + 1, i + r - 1, x };
if (str[i] == str[i + 1]) ++lst;
else if (lst > 1) runs[++tot] = Runs { i - lst + 1, i, 1 }, lst = 1;
}
if (lst > 1) runs[++tot] = Runs { n - lst + 1, n, 1 };
sort(runs + 1, runs + 1 + tot);
tot = unique(runs + 1, runs + 1 + tot) - runs - 1;
for (int i = 1; i <= tot; i++) {
const Runs &r = runs[i];
vr[r.r].emplace_back(r);
for (int j = r.l + r.p * 2 - 1; j <= r.r; j++)
md[j].emplace_back(i);
}
}
}
void solve() {
for (int i = 1; i <= n; i++) {
for (int j : md[i]) {
const Runs &r = runs[j];
int np = r.p << 1, t = (i - r.l + 1) / np, l = i - t * np + 1;
LL h = Hash::get_hash(l, i);
int &p = HM::get(h);
if (p != 0) add(p, -1);
p = 0;
}
int d = ask(i);
for (const Qry &q : qry[i]) {
ans[q.id] = d - ask(q.l - 1);
for (int j : md[i]) {
int l = i - max(runs[j].l, q.l) + 1, p = runs[j].p;
int t = l % (p << 1), s = l / (p << 1);
if (s == 0) continue;
if (t >= p - 1) ans[q.id] += s * p;
else ans[q.id] += s * (t + 1) + (s - 1) * (p - t - 1);
}
}
for (const Runs &r : vr[i]) {
// printf("%d %d %d\n", r.l, r.r, r.p);
int np = r.p << 1;
for (int j = r.l; j + np - 1 <= r.r; j++) {
int t = (r.r - j + 1) / np, k = j + t * np - 1;
if (r.r - k >= r.p) continue;
LL h = Hash::get_hash(j, k);
int &p = HM::get(h);
add(j, 1), p = j;
}
}
}
}
int main() {
IO::read(n, m);
IO::read(str + 1);
Hash::init();
SA::init(str);
for (int i = 1; i <= m; i++) {
int l, r; IO::read(l, r);
qry[r].emplace_back(Qry { l, i });
}
solve();
for (int i = 1; i <= m; i++) IO::print(ans[i]);
IO::ioflush();
return 0;
}