Solution S o l u t i o n
O(nn−−√logn) O ( n n log n ) 的莫队连 50000 50000 都过不了qwq。
%%%gjghfd
gjghfd教我SAM+树上启发式合并的做法。
每两个后缀的贡献会在LCA处统计到。
一个暴力的想法就是枚举LCA,那就只需要考虑子树中的点对。有实际贡献意义的就是两个编号最近的点对。
考虑树上启发式合并,用线段树维护已加入的点,每次加入一个新的点的时候只要找到左右相应的最近的点。会得到一些三元组
(x,y,deplca(x,y))
(
x
,
y
,
d
e
p
lca
(
x
,
y
)
)
。
然后就是一个类似离线数点的过程。
two-pointers
two-pointers
扫左端点,树状数组维护右端点就好了。
#include <bits/stdc++.h>
#define show(x) cerr << #x << " = " << x << endl
using namespace std;
typedef long long ll;
typedef pair<int, int> pairs;
const int K = 18;
const int N = 202020;
inline char get(void) {
static char buf[100000], *S = buf, *T = buf;
if (S == T) {
T = (S = buf) + fread(buf, 1, 100000, stdin);
if (S == T) return EOF;
}
return *S++;
}
template<typename T>
inline void read(T &x) {
static char c; x = 0; int sgn = 0;
for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
if (sgn) x = -x;
}
inline void read(char *c) {
for (*c = get(); *c < '0' || *c > '1'; *c = get());
for (; *c >= '0' && *c <= '1'; *c = get()) ++c;
*c = 0;
}
namespace SAM {
int last, tcnt, root;
int id[N], rgt[N];
int mx[N], par[N];
int go[N][2];
inline void init(void) {
last = tcnt = root = 1;
}
inline int extend(int c, int i) {
int p = last, np = ++tcnt;
id[np] = i; rgt[np] = 1;
mx[np] = mx[p] + 1;
for (; !go[p][c] && p; p = par[p]) go[p][c] = np;
if (p) {
int q = go[p][c];
if (mx[q] == mx[p] + 1) {
par[np] = q;
} else {
int nq = ++tcnt;
mx[nq] = mx[p] + 1;
par[nq] = par[q];
par[q] = par[np] = nq;
memcpy(go[nq], go[q], sizeof go[q]);
for (; go[p][c] == q; p = par[p])
go[p][c] = nq;
}
} else {
par[np] = root;
}
return last = np;
}
}
namespace BIT {
int c[N];
int maxn;
inline void init(int n) {
maxn = n;
}
inline void add(int x, int a) {
for (; x <= maxn; x += x & -x)
c[x] = max(c[x], a);
}
inline int query(int x) {
int mx = 0;
for (; x; x -= x & -x)
mx = max(mx, c[x]);
return mx;
}
}
namespace segTree {
int sm[N << 2];
inline void insert(int o, int l, int r, int pos, int x) {
sm[o] += x;
if (l == r) return;
int mid = (l + r) >> 1;
if (pos <= mid) insert(o << 1, l, mid, pos, x);
else insert(o << 1 | 1, mid + 1, r, pos, x);
}
inline int findL(int o, int l, int r, int pos) {
if (sm[o] == 0 || l > pos) return -1;
if (l == r) return l;
int mid = (l + r) >> 1;
if (r <= pos) {
if (!sm[o << 1 | 1]) return findL(o << 1, l, mid, pos);
return findL(o << 1 | 1, mid + 1, r, pos);
}
int res = findL(o << 1 | 1, mid + 1, r, pos);
if (~res) return res;
return findL(o << 1, l, mid, pos);
}
inline int findR(int o, int l, int r, int pos) {
if (sm[o] == 0 || r < pos) return -1;
if (l == r) return l;
int mid = (l + r) >> 1;
if (l >= pos) {
if (!sm[o << 1]) return findR(o << 1 | 1, mid + 1, r, pos);
return findR(o << 1, l, mid, pos);
}
int res = findR(o << 1, l, mid, pos);
if (~res) return res;
return findR(o << 1 | 1, mid + 1, r, pos);
}
}
using SAM::id;
using segTree::insert;
using segTree::findL;
using segTree::findR;
int n, m, clc;
char s[N];
struct qry {
int l, r, id;
inline bool operator <(const qry &b) const {
return l > b.l;
}
} q[N];
int size[N], fa[N], son[N], dep[N];
int pre[N], post[N], erp[N];
int ans[N];
vector<int> G[N];
vector<pairs> p[N];
inline void addEdge(int from, int to) {
G[from].push_back(to);
}
inline void build(void) {
using namespace SAM;
for (int i = 1; i <= tcnt; i++) {
if (fa[i] = par[i])
addEdge(par[i], i);
size[i] = rgt[i];
dep[i] = mx[i];
}
BIT::init(n);
}
inline void dfs1(int u) {
erp[pre[u] = ++clc] = u;
for (int to: G[u]) {
dfs1(to); size[u] += size[to];
if (size[son[u]] < size[to])
son[u] = to;
}
post[u] = clc;
}
inline void insert(int x, int mx) {
int L = findL(1, 1, n, x - 1);
int R = findR(1, 1, n, x + 1);
if (~L) p[L].push_back(pairs(x, mx));
if (~R) p[x].push_back(pairs(R, mx));
insert(1, 1, n, x, 1);
}
inline void dfs(int u, int iw) {
for (int to: G[u])
if (to != son[u]) dfs(to, 0);
if (son[u]) dfs(son[u], 1);
for (int to: G[u])
if (to != son[u])
for (int i = pre[to]; i <= post[to]; i++) {
int v = erp[i];
if (id[v]) insert(id[v], dep[u]);
}
if (id[u]) insert(id[u], dep[u]);
if (iw == 0)
for (int i = pre[u]; i <= post[u]; i++) {
int v = erp[i];
if (id[v]) insert(1, 1, n, id[v], -1);
}
}
int main(void) {
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
read(n); read(m);
read(s);
SAM::init();
for (int i = 0; i < n; i++)
SAM::extend(s[i] - '0', i + 1);
build(); dfs1(1); dfs(1, 0);
for (int i = 1; i <= m; i++) {
read(q[i].l); read(q[i].r);
q[i].id = i;
}
sort(q + 1, q + m + 1);
int j = n;
for (int i = 1; i <= m; i++) {
for (; j >= q[i].l; j--)
for (auto u: p[j])
BIT::add(u.first, u.second);
ans[q[i].id] = BIT::query(q[i].r);
}
for (int i = 1; i <= m; i++)
printf("%d\n", ans[i]);
return 0;
}